偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

JavaScript 的一些常用設(shè)計(jì)模式

開(kāi)發(fā) 前端
設(shè)計(jì)模式的定義:在面向?qū)ο筌浖O(shè)計(jì)過(guò)程中針對(duì)特定問(wèn)題的簡(jiǎn)潔而優(yōu)雅的解決方案

設(shè)計(jì)模式是前人解決某個(gè)特定場(chǎng)景下對(duì)而總結(jié)出來(lái)的一些解決方案。可能剛開(kāi)始接觸編程還沒(méi)有什么經(jīng)驗(yàn)的時(shí)候,會(huì)感覺(jué)設(shè)計(jì)模式?jīng)]那么好理解,這個(gè)也很正常。有些簡(jiǎn)單的設(shè)計(jì)模式我們有時(shí)候用到,不過(guò)沒(méi)意識(shí)到也是存在的。

[[274128]]

學(xué)習(xí)設(shè)計(jì)模式,可以讓我們?cè)谔幚韱?wèn)題的時(shí)候提供更多更快的解決思路。

當(dāng)然設(shè)計(jì)模式的應(yīng)用也不是一時(shí)半會(huì)就會(huì)上手,很多情況下我們編寫(xiě)的業(yè)務(wù)邏輯都沒(méi)用到設(shè)計(jì)模式或者本來(lái)就不需要特定的設(shè)計(jì)模式。

適配器模式

這個(gè)使我們常使用的設(shè)計(jì)模式,也算最簡(jiǎn)單的設(shè)計(jì)模式之一,好處在于可以保持原有接口的數(shù)據(jù)結(jié)構(gòu)不變動(dòng)。

適配器模式(Adapter Pattern)是作為兩個(gè)不兼容的接口之間的橋梁。

例子

適配器模式很好理解,假設(shè)我們和后端定義了一個(gè)接口數(shù)據(jù)結(jié)構(gòu)為(可以理解為舊接口):

  1.   { 
  2.     "label""選擇一"
  3.     "value": 0 
  4.   }, 
  5.   { 
  6.     "label""選擇二"
  7.     "value": 1 
  8.   } 

但是后端后面因?yàn)槠渌?,需要定義返回的結(jié)構(gòu)為(可以理解為新接口):

  1.   { 
  2.     "label""選擇一"
  3.     "text": 0 
  4.   }, 
  5.   { 
  6.     "label""選擇二"
  7.     "text": 1 
  8.   } 

然后我們前端的使用到后端接口有好幾處,那么我可以把新的接口字段結(jié)構(gòu)適配為老接口的,就不需要各處去修改字段,只要把源頭的數(shù)據(jù)適配好就可以了。

當(dāng)然上面的是非常簡(jiǎn)單的場(chǎng)景,也是經(jīng)常用到的場(chǎng)景?;蛟S你會(huì)認(rèn)為后端處理不更好了,的確是這樣更好,但是這個(gè)不是我們討論的范圍。

單例模式

單例模式,從字面意思也很好理解,就是實(shí)例化多次都只會(huì)有一個(gè)實(shí)例。

有些場(chǎng)景實(shí)例化一次,可以達(dá)到緩存效果,可以減少內(nèi)存占用。還有些場(chǎng)景就是必須只能實(shí)例化一次,否則實(shí)例化多次會(huì)覆蓋之前的實(shí)例,導(dǎo)致出現(xiàn) bug(這種場(chǎng)景比較少見(jiàn))。

例子

實(shí)現(xiàn)彈框的一種做法是先創(chuàng)建好彈框, 然后使之隱藏, 這樣子的話會(huì)浪費(fèi)部分不必要的 DOM 開(kāi)銷(xiāo), 我們可以在需要彈框的時(shí)候再進(jìn)行創(chuàng)建, 同時(shí)結(jié)合單例模式實(shí)現(xiàn)只有一個(gè)實(shí)例, 從而節(jié)省部分 DOM 開(kāi)銷(xiāo)。下列為登入框部分代碼:

  1. const createLoginLayer = function() { 
  2.   const div = document.createElement('div'
  3.   div.innerHTML = '登入浮框' 
  4.   div.style.display = 'none' 
  5.   document.body.appendChild(div) 
  6.   return div 

使單例模式和創(chuàng)建彈框代碼解耦

  1. const getSingle = function(fn) { 
  2.   const result 
  3.   return function() { 
  4.     return result || result = fn.apply(this, arguments) 
  5.   } 
  6. const createSingleLoginLayer = getSingle(createLoginLayer) 
  7.  
  8. document.getElementById('loginBtn').onclick = function() { 
  9.   createSingleLoginLayer() 

代理模式

代理模式的定義:為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問(wèn)。

代理對(duì)象擁有本體對(duì)象的一切功能的同時(shí),可以擁有而外的功能。而且代理對(duì)象和本體對(duì)象具有一致的接口,對(duì)使用者友好。

虛擬代理

下面這段代碼運(yùn)用代理模式來(lái)實(shí)現(xiàn)圖片預(yù)加載,可以看到通過(guò)代理模式巧妙地將創(chuàng)建圖片與預(yù)加載邏輯分離,,并且在未來(lái)如果不需要預(yù)加載,只要改成請(qǐng)求本體代替請(qǐng)求代理對(duì)象就行。

  1. const myImage = (function() { 
  2.   const imgNode = document.createElement('img'
  3.   document.body.appendChild(imgNode) 
  4.   return { 
  5.     setSrc: function(src) { 
  6.       imgNode.src = src 
  7.     } 
  8.   } 
  9. })() 
  10.  
  11. const proxyImage = (function() { 
  12.   const img = new Image() 
  13.   img.onload = function() { // http 圖片加載完畢后才會(huì)執(zhí)行 
  14.     myImage.setSrc(this.src) 
  15.   } 
  16.   return { 
  17.     setSrc: function(src) { 
  18.       myImage.setSrc('loading.jpg') // 本地 loading 圖片 
  19.       img.src = src 
  20.     } 
  21.   } 
  22. })() 
  23.  
  24. proxyImage.setSrc('http://loaded.jpg'

緩存代理

在原有的功能上加上結(jié)果緩存功能,就屬于緩存代理。

原先有個(gè)功能是實(shí)現(xiàn)字符串反轉(zhuǎn)(reverseString),那么在不改變 reverseString 的現(xiàn)有邏輯,我們可以使用緩存代理模式實(shí)現(xiàn)性能的優(yōu)化,當(dāng)然也可以在值改變的時(shí)候去處理下其他邏輯,如 Vue computed 的用法。

  1. function reverseString(str) { 
  2.   return str 
  3.     .split(''
  4.     .reverse() 
  5.     .join(''
  6. const reverseStringProxy = (function() { 
  7.   const cached = {} 
  8.   return function(str) { 
  9.     if (cached[str]) { 
  10.       return cached[str] 
  11.     } 
  12.     cached[str] = reverseString(str) 
  13.     return cached[str] 
  14.   } 
  15. })() 

訂閱發(fā)布模式

訂閱發(fā)布使前端常用的數(shù)據(jù)通信方式、異步邏輯處理等等,如 React setState 和 Redux 就是訂閱發(fā)布模式的。

但是要合理的使用訂閱發(fā)布模式,否則會(huì)造成數(shù)據(jù)混亂,redux 的單向數(shù)據(jù)流思想可以避免數(shù)據(jù)流混亂的問(wèn)題。

例子

  1. class Event { 
  2.   constructor() { 
  3.     // 所有 eventType 監(jiān)聽(tīng)器回調(diào)函數(shù)(數(shù)組) 
  4.     this.listeners = {} 
  5.   } 
  6.   /** 
  7.    * 訂閱事件 
  8.    * @param {String} eventType 事件類型 
  9.    * @param {Function} listener 訂閱后發(fā)布動(dòng)作觸發(fā)的回調(diào)函數(shù),參數(shù)為發(fā)布的數(shù)據(jù) 
  10.    */ 
  11.   on(eventType, listener) { 
  12.     if (!this.listeners[eventType]) { 
  13.       this.listeners[eventType] = [] 
  14.     } 
  15.     this.listeners[eventType].push(listener) 
  16.   } 
  17.   /** 
  18.    * 發(fā)布事件 
  19.    * @param {String} eventType 事件類型 
  20.    * @param {Any} data 發(fā)布的內(nèi)容 
  21.    */ 
  22.   emit(eventType, data) { 
  23.     const callbacks = this.listeners[eventType] 
  24.     if (callbacks) { 
  25.       callbacks.forEach((c) => { 
  26.         c(data) 
  27.       }) 
  28.     } 
  29.   } 
  30.  
  31. const event = new Event() 
  32. event.on('open', (data) => { 
  33.   console.log(data) 
  34. }) 
  35. event.emit('open', { opentrue }) 

觀察者模式

觀察者模式定義了一種一對(duì)多的依賴關(guān)系,讓多個(gè)觀察者對(duì)象同時(shí)監(jiān)聽(tīng)某一個(gè)目標(biāo)對(duì)象,當(dāng)這個(gè)目標(biāo)對(duì)象的狀態(tài)發(fā)生變化時(shí),會(huì)通知所有觀察者對(duì)象,使它們能夠自動(dòng)更新。

Vue 的數(shù)據(jù)驅(qū)動(dòng)就是使用觀察者模式,mbox 也是使用觀察者模式。

例子

模仿 Vue 數(shù)據(jù)驅(qū)動(dòng)渲染模式(只是類似,簡(jiǎn)單的模仿)。

首先使用 setter 和 getter 監(jiān)聽(tīng)到數(shù)據(jù)的變化:

  1. const obj = { 
  2.   data: { description: '' }, 
  3.  
  4. Object.defineProperty(obj, 'description', { 
  5.   get() { 
  6.     return this.data.description 
  7.   }, 
  8.   set(val) { 
  9.     this.data.description = val 
  10.   }, 
  11. }) 

然后加上目標(biāo)和觀察者

  1. class Subject { 
  2.   constructor() { 
  3.     this.observers = [] 
  4.   } 
  5.  
  6.   add(observer) { 
  7.     this.observers.push(observer) 
  8.   } 
  9.  
  10.   notify(data) { 
  11.     this.observers.forEach((observer) => observer.update(data)) 
  12.   } 
  13.  
  14. class Observer { 
  15.   constructor(callback) { 
  16.     this.callback = callback 
  17.   } 
  18.   update(data) { 
  19.     this.callback && this.callback(data) 
  20.   } 
  21.  
  22. // 創(chuàng)建觀察者ob1 
  23. let ob1 = new Observer((text) => { 
  24.   document.querySelector('#dom-one').innerHTML(text) 
  25. }) 
  26. // 創(chuàng)建觀察者ob2 
  27. let ob2 = new Observer((text) => { 
  28.   document.querySelector('#dom-two').innerHTML(text) 
  29. }) 
  30. // 創(chuàng)建目標(biāo)sub 
  31. let sub = new Subject() 
  32. // 目標(biāo)sub添加觀察者ob1 (目標(biāo)和觀察者建立了依賴關(guān)系) 
  33. sub.add(ob1) 
  34. // 目標(biāo)sub添加觀察者ob2 
  35. sub.add(ob2) 
  36. // 目標(biāo)sub觸發(fā)事件(目標(biāo)主動(dòng)通知觀察者) 
  37. sub.notify('這里改變了'

組合在一起是這樣的

  1. <!DOCTYPE html> 
  2. <html> 
  3.   <head> 
  4.     <meta charset="utf-8" /> 
  5.     <meta 
  6.       name="viewport" 
  7.       content="width=device-width,initial-scale=1,maximum-scale=1,viewport-fit=cover" 
  8.     /> 
  9.     <title></title> 
  10.   </head> 
  11.   <body> 
  12.     <div id="app"
  13.       <div id="dom-one"
  14.         原來(lái)的值 
  15.       </div> 
  16.       <br /> 
  17.       <div id="dom-two"
  18.         原來(lái)的值 
  19.       </div> 
  20.       <br /> 
  21.       <button id="btn">改變</button> 
  22.     </div> 
  23.     <script> 
  24.       class Subject { 
  25.         constructor() { 
  26.           this.observers = [] 
  27.         } 
  28.  
  29.         add(observer) { 
  30.           this.observers.push(observer) 
  31.         } 
  32.  
  33.         notify() { 
  34.           this.observers.forEach((observer) => observer.update()) 
  35.         } 
  36.       } 
  37.  
  38.       class Observer { 
  39.         constructor(callback) { 
  40.           this.callback = callback 
  41.         } 
  42.         update() { 
  43.           this.callback && this.callback() 
  44.         } 
  45.       } 
  46.  
  47.       const obj = { 
  48.         data: { description: '' }, 
  49.       } 
  50.  
  51.       // 創(chuàng)建觀察者ob1 
  52.       const ob1 = new Observer(() => { 
  53.         console.log(document.querySelector('#dom-one')) 
  54.         document.querySelector('#dom-one').innerHTML = obj.description 
  55.       }) 
  56.       // 創(chuàng)建觀察者ob2 
  57.       const ob2 = new Observer(() => { 
  58.         document.querySelector('#dom-two').innerHTML = obj.description 
  59.       }) 
  60.       // 創(chuàng)建目標(biāo)sub 
  61.       const sub = new Subject() 
  62.       // 目標(biāo)sub添加觀察者ob1 (目標(biāo)和觀察者建立了依賴關(guān)系) 
  63.       sub.add(ob1) 
  64.       // 目標(biāo)sub添加觀察者ob2 
  65.       sub.add(ob2) 
  66.  
  67.       Object.defineProperty(obj, 'description', { 
  68.         get() { 
  69.           return this.data.description 
  70.         }, 
  71.         set(val) { 
  72.           this.data.description = val 
  73.           // 目標(biāo)sub觸發(fā)事件(目標(biāo)主動(dòng)通知觀察者) 
  74.           sub.notify() 
  75.         }, 
  76.       }) 
  77.       btn.onclick = () => { 
  78.         obj.description = '改變了' 
  79.       } 
  80.     </script> 
  81.   </body> 
  82. </html> 

裝飾者模式

裝飾器模式(Decorator Pattern)允許向一個(gè)現(xiàn)有的對(duì)象添加新的功能,同時(shí)又不改變其結(jié)構(gòu)。

ES6/7 的decorator 語(yǔ)法提案,就是裝飾者模式。

例子

  1. class A { 
  2.   getContent() { 
  3.     return '第一行內(nèi)容' 
  4.   } 
  5.   render() { 
  6.     document.body.innerHTML = this.getContent() 
  7.   } 
  8.  
  9. function decoratorOne(cla) { 
  10.   const prevGetContent = cla.prototype.getContent 
  11.   cla.prototype.getContent = function() { 
  12.     return ` 
  13.       第一行之前的內(nèi)容 
  14.       <br/> 
  15.       ${prevGetContent()} 
  16.     ` 
  17.   } 
  18.   return cla 
  19.  
  20. function decoratorTwo(cla) { 
  21.   const prevGetContent = cla.prototype.getContent 
  22.   cla.prototype.getContent = function() { 
  23.     return ` 
  24.       ${prevGetContent()} 
  25.       <br/> 
  26.       第二行內(nèi)容 
  27.     ` 
  28.   } 
  29.   return cla 
  30.  
  31. const B = decoratorOne(A) 
  32. const C = decoratorTwo(B) 
  33. new C().render() 

策略模式

在策略模式(Strategy Pattern)中,一個(gè)行為或其算法可以在運(yùn)行時(shí)更改。

假設(shè)我們的績(jī)效分為 A、B、C、D 這四個(gè)等級(jí),四個(gè)等級(jí)的獎(jiǎng)勵(lì)是不一樣的,一般我們的代碼是這樣實(shí)現(xiàn):

  1. /** 
  2.  
  3. * 獲取年終獎(jiǎng) 
  4.  
  5. * @param {String} performanceType 績(jī)效類型, 
  6.  
  7. * @return {Object} 年終獎(jiǎng),包括獎(jiǎng)金和獎(jiǎng)品 
  8.  
  9. */ 
  10.  
  11. function getYearEndBonus(performanceType) { 
  12.  
  13. const yearEndBonus = { 
  14.  
  15. // 獎(jiǎng)金 
  16.  
  17. bonus: ''
  18.  
  19. // 獎(jiǎng)品 
  20.  
  21. prize: ''
  22.  
  23.  
  24. switch (performanceType) { 
  25.  
  26. case 'A': { 
  27.  
  28. yearEndBonus = { 
  29.  
  30. bonus: 50000, 
  31.  
  32. prize: 'mac pro'
  33.  
  34.  
  35. break 
  36.  
  37.  
  38. case 'B': { 
  39.  
  40. yearEndBonus = { 
  41.  
  42. bonus: 40000, 
  43.  
  44. prize: 'mac air'
  45.  
  46.  
  47. break 
  48.  
  49.  
  50. case 'C': { 
  51.  
  52. yearEndBonus = { 
  53.  
  54. bonus: 20000, 
  55.  
  56. prize: 'iphone xr'
  57.  
  58.  
  59. break 
  60.  
  61.  
  62. case 'D': { 
  63.  
  64. yearEndBonus = { 
  65.  
  66. bonus: 5000, 
  67.  
  68. prize: 'ipad mini'
  69.  
  70.  
  71. break 
  72.  
  73.  
  74.  
  75. return yearEndBonus 
  76.  

使用策略模式可以這樣:

  1. /** 
  2.  * 獲取年終獎(jiǎng) 
  3.  * @param {String} strategyFn 績(jī)效策略函數(shù) 
  4.  * @return {Object} 年終獎(jiǎng),包括獎(jiǎng)金和獎(jiǎng)品 
  5.  */ 
  6. function getYearEndBonus(strategyFn) { 
  7.   if (!strategyFn) { 
  8.     return {} 
  9.   } 
  10.   return strategyFn() 
  11.  
  12. const bonusStrategy = { 
  13.   A() { 
  14.     return { 
  15.       bonus: 50000, 
  16.       prize: 'mac pro'
  17.     } 
  18.   }, 
  19.   B() { 
  20.     return { 
  21.       bonus: 40000, 
  22.       prize: 'mac air'
  23.     } 
  24.   }, 
  25.   C() { 
  26.     return { 
  27.       bonus: 20000, 
  28.       prize: 'iphone xr'
  29.     } 
  30.   }, 
  31.   D() { 
  32.     return { 
  33.       bonus: 10000, 
  34.       prize: 'ipad mini'
  35.     } 
  36.   }, 
  37.  
  38. const performanceLevel = 'A' 
  39. getYearEndBonus(bonusStrategy[performanceLevel]) 

這里每個(gè)函數(shù)就是一個(gè)策略,修改一個(gè)其中一個(gè)策略,并不會(huì)影響其他的策略,都可以單獨(dú)使用。當(dāng)然這只是個(gè)簡(jiǎn)單的范例,只為了說(shuō)明。

策略模式比較明顯的特性就是可以減少 if 語(yǔ)句或者 switch 語(yǔ)句。

職責(zé)鏈模式

顧名思義,責(zé)任鏈模式(Chain of Responsibility Pattern)為請(qǐng)求創(chuàng)建了一個(gè)接收者對(duì)象的鏈。這種模式給予請(qǐng)求的類型,對(duì)請(qǐng)求的發(fā)送者和接收者進(jìn)行解耦。這種類型的設(shè)計(jì)模式屬于行為型模式。

在這種模式中,通常每個(gè)接收者都包含對(duì)另一個(gè)接收者的引用。如果一個(gè)對(duì)象不能處理該請(qǐng)求,那么它會(huì)把相同的請(qǐng)求傳給下一個(gè)接收者,依此類推。

例子

  1. function order(options) { 
  2.   return { 
  3.     next: (callback) => callback(options), 
  4.   } 
  5.  
  6. function order500(options) { 
  7.   const { orderType, pay } = options 
  8.   if (orderType === 1 && pay === true) { 
  9.     console.log('500 元定金預(yù)購(gòu), 得到 100 元優(yōu)惠券'
  10.     return { 
  11.       next: () => {}, 
  12.     } 
  13.   } else { 
  14.     return { 
  15.       next: (callback) => callback(options), 
  16.     } 
  17.   } 
  18.  
  19. function order200(options) { 
  20.   const { orderType, pay } = options 
  21.   if (orderType === 2 && pay === true) { 
  22.     console.log('200 元定金預(yù)購(gòu), 得到 50 元優(yōu)惠券'
  23.     return { 
  24.       next: () => {}, 
  25.     } 
  26.   } else { 
  27.     return { 
  28.       next: (callback) => callback(options), 
  29.     } 
  30.   } 
  31.  
  32. function orderCommon(options) { 
  33.   const { orderType, stock } = options 
  34.   if (orderType === 3 && stock > 0) { 
  35.     console.log('普通購(gòu)買(mǎi), 無(wú)優(yōu)惠券'
  36.     return {} 
  37.   } else { 
  38.     console.log('庫(kù)存不夠, 無(wú)法購(gòu)買(mǎi)'
  39.   } 
  40.  
  41. order({ 
  42.   orderType: 3, 
  43.   pay: true
  44.   stock: 500, 
  45. }) 
  46.   .next(order500) 
  47.   .next(order200) 
  48.   .next(orderCommon) 
  49. // 打印出 “普通購(gòu)買(mǎi), 無(wú)優(yōu)惠券” 

上面的代碼,對(duì) order 相關(guān)的進(jìn)行了解耦,order500,order200、orderCommon 等都是可以單獨(dú)調(diào)用的。

責(zé)任編輯:華軒 來(lái)源: segmentfault
相關(guān)推薦

2017-04-08 17:12:36

設(shè)計(jì)模式抽象策略模式

2012-10-29 11:16:13

2011-03-15 17:46:43

2010-09-28 14:14:19

SQL語(yǔ)句

2021-10-13 07:48:23

Options模式編程

2011-07-13 09:13:56

Android設(shè)計(jì)

2022-02-17 13:58:38

Linux技巧文件

2012-06-07 10:17:55

軟件設(shè)計(jì)設(shè)計(jì)原則Java

2009-09-27 11:09:42

API設(shè)計(jì)

2022-12-02 14:58:27

JavaScript技巧編程

2014-08-14 09:25:31

Linux串口

2015-08-27 10:49:43

JavaScript開(kāi)發(fā)框架

2020-10-19 19:25:32

Python爬蟲(chóng)代碼

2021-08-28 11:47:52

json解析

2022-03-22 07:38:00

SQL語(yǔ)句MySQL

2014-05-13 09:55:13

iOS開(kāi)發(fā)工具

2010-10-08 16:32:59

MySQL語(yǔ)句

2021-12-20 10:55:05

Git命令Linux

2012-03-14 14:30:13

Ubuntu軟件包

2009-08-27 11:02:22

JavaScript事
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)