成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JavaScript 的一些常用設計模式

開發 前端
設計模式的定義:在面向對象軟件設計過程中針對特定問題的簡潔而優雅的解決方案

設計模式是前人解決某個特定場景下對而總結出來的一些解決方案。可能剛開始接觸編程還沒有什么經驗的時候,會感覺設計模式沒那么好理解,這個也很正常。有些簡單的設計模式我們有時候用到,不過沒意識到也是存在的。

[[274128]]

學習設計模式,可以讓我們在處理問題的時候提供更多更快的解決思路。

當然設計模式的應用也不是一時半會就會上手,很多情況下我們編寫的業務邏輯都沒用到設計模式或者本來就不需要特定的設計模式。

適配器模式

這個使我們常使用的設計模式,也算最簡單的設計模式之一,好處在于可以保持原有接口的數據結構不變動。

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

例子

適配器模式很好理解,假設我們和后端定義了一個接口數據結構為(可以理解為舊接口):

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

但是后端后面因為其他原因,需要定義返回的結構為(可以理解為新接口):

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

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

當然上面的是非常簡單的場景,也是經常用到的場景。或許你會認為后端處理不更好了,的確是這樣更好,但是這個不是我們討論的范圍。

單例模式

單例模式,從字面意思也很好理解,就是實例化多次都只會有一個實例。

有些場景實例化一次,可以達到緩存效果,可以減少內存占用。還有些場景就是必須只能實例化一次,否則實例化多次會覆蓋之前的實例,導致出現 bug(這種場景比較少見)。

例子

實現彈框的一種做法是先創建好彈框, 然后使之隱藏, 這樣子的話會浪費部分不必要的 DOM 開銷, 我們可以在需要彈框的時候再進行創建, 同時結合單例模式實現只有一個實例, 從而節省部分 DOM 開銷。下列為登入框部分代碼:

  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 

使單例模式和創建彈框代碼解耦

  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() 

代理模式

代理模式的定義:為一個對象提供一個代用品或占位符,以便控制對它的訪問。

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

虛擬代理

下面這段代碼運用代理模式來實現圖片預加載,可以看到通過代理模式巧妙地將創建圖片與預加載邏輯分離,,并且在未來如果不需要預加載,只要改成請求本體代替請求代理對象就行。

  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 圖片加載完畢后才會執行 
  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'

緩存代理

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

原先有個功能是實現字符串反轉(reverseString),那么在不改變 reverseString 的現有邏輯,我們可以使用緩存代理模式實現性能的優化,當然也可以在值改變的時候去處理下其他邏輯,如 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. })() 

訂閱發布模式

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

但是要合理的使用訂閱發布模式,否則會造成數據混亂,redux 的單向數據流思想可以避免數據流混亂的問題。

例子

  1. class Event { 
  2.   constructor() { 
  3.     // 所有 eventType 監聽器回調函數(數組) 
  4.     this.listeners = {} 
  5.   } 
  6.   /** 
  7.    * 訂閱事件 
  8.    * @param {String} eventType 事件類型 
  9.    * @param {Function} listener 訂閱后發布動作觸發的回調函數,參數為發布的數據 
  10.    */ 
  11.   on(eventType, listener) { 
  12.     if (!this.listeners[eventType]) { 
  13.       this.listeners[eventType] = [] 
  14.     } 
  15.     this.listeners[eventType].push(listener) 
  16.   } 
  17.   /** 
  18.    * 發布事件 
  19.    * @param {String} eventType 事件類型 
  20.    * @param {Any} data 發布的內容 
  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 }) 

觀察者模式

觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個目標對象,當這個目標對象的狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新。

Vue 的數據驅動就是使用觀察者模式,mbox 也是使用觀察者模式。

例子

模仿 Vue 數據驅動渲染模式(只是類似,簡單的模仿)。

首先使用 setter 和 getter 監聽到數據的變化:

  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. }) 

然后加上目標和觀察者

  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. // 創建觀察者ob1 
  23. let ob1 = new Observer((text) => { 
  24.   document.querySelector('#dom-one').innerHTML(text) 
  25. }) 
  26. // 創建觀察者ob2 
  27. let ob2 = new Observer((text) => { 
  28.   document.querySelector('#dom-two').innerHTML(text) 
  29. }) 
  30. // 創建目標sub 
  31. let sub = new Subject() 
  32. // 目標sub添加觀察者ob1 (目標和觀察者建立了依賴關系) 
  33. sub.add(ob1) 
  34. // 目標sub添加觀察者ob2 
  35. sub.add(ob2) 
  36. // 目標sub觸發事件(目標主動通知觀察者) 
  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.         原來的值 
  15.       </div> 
  16.       <br /> 
  17.       <div id="dom-two"
  18.         原來的值 
  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.       // 創建觀察者ob1 
  52.       const ob1 = new Observer(() => { 
  53.         console.log(document.querySelector('#dom-one')) 
  54.         document.querySelector('#dom-one').innerHTML = obj.description 
  55.       }) 
  56.       // 創建觀察者ob2 
  57.       const ob2 = new Observer(() => { 
  58.         document.querySelector('#dom-two').innerHTML = obj.description 
  59.       }) 
  60.       // 創建目標sub 
  61.       const sub = new Subject() 
  62.       // 目標sub添加觀察者ob1 (目標和觀察者建立了依賴關系) 
  63.       sub.add(ob1) 
  64.       // 目標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.           // 目標sub觸發事件(目標主動通知觀察者) 
  74.           sub.notify() 
  75.         }, 
  76.       }) 
  77.       btn.onclick = () => { 
  78.         obj.description = '改變了' 
  79.       } 
  80.     </script> 
  81.   </body> 
  82. </html> 

裝飾者模式

裝飾器模式(Decorator Pattern)允許向一個現有的對象添加新的功能,同時又不改變其結構。

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

例子

  1. class A { 
  2.   getContent() { 
  3.     return '第一行內容' 
  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.       第一行之前的內容 
  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.       第二行內容 
  27.     ` 
  28.   } 
  29.   return cla 
  30.  
  31. const B = decoratorOne(A) 
  32. const C = decoratorTwo(B) 
  33. new C().render() 

策略模式

在策略模式(Strategy Pattern)中,一個行為或其算法可以在運行時更改。

假設我們的績效分為 A、B、C、D 這四個等級,四個等級的獎勵是不一樣的,一般我們的代碼是這樣實現:

  1. /** 
  2.  
  3. * 獲取年終獎 
  4.  
  5. * @param {String} performanceType 績效類型, 
  6.  
  7. * @return {Object} 年終獎,包括獎金和獎品 
  8.  
  9. */ 
  10.  
  11. function getYearEndBonus(performanceType) { 
  12.  
  13. const yearEndBonus = { 
  14.  
  15. // 獎金 
  16.  
  17. bonus: ''
  18.  
  19. // 獎品 
  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.  * 獲取年終獎 
  3.  * @param {String} strategyFn 績效策略函數 
  4.  * @return {Object} 年終獎,包括獎金和獎品 
  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]) 

這里每個函數就是一個策略,修改一個其中一個策略,并不會影響其他的策略,都可以單獨使用。當然這只是個簡單的范例,只為了說明。

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

職責鏈模式

顧名思義,責任鏈模式(Chain of Responsibility Pattern)為請求創建了一個接收者對象的鏈。這種模式給予請求的類型,對請求的發送者和接收者進行解耦。這種類型的設計模式屬于行為型模式。

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

例子

  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 元定金預購, 得到 100 元優惠券'
  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 元定金預購, 得到 50 元優惠券'
  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('普通購買, 無優惠券'
  36.     return {} 
  37.   } else { 
  38.     console.log('庫存不夠, 無法購買'
  39.   } 
  40.  
  41. order({ 
  42.   orderType: 3, 
  43.   pay: true
  44.   stock: 500, 
  45. }) 
  46.   .next(order500) 
  47.   .next(order200) 
  48.   .next(orderCommon) 
  49. // 打印出 “普通購買, 無優惠券” 

上面的代碼,對 order 相關的進行了解耦,order500,order200、orderCommon 等都是可以單獨調用的。

責任編輯:華軒 來源: segmentfault
相關推薦

2017-04-08 17:12:36

設計模式抽象策略模式

2012-10-29 11:16:13

2010-09-28 14:14:19

SQL語句

2011-03-15 17:46:43

2021-10-13 07:48:23

Options模式編程

2011-07-13 09:13:56

Android設計

2022-12-02 14:58:27

JavaScript技巧編程

2022-02-17 13:58:38

Linux技巧文件

2012-06-07 10:17:55

軟件設計設計原則Java

2009-09-27 11:09:42

API設計

2015-08-27 10:49:43

JavaScript開發框架

2014-08-14 09:25:31

Linux串口

2021-12-20 10:55:05

Git命令Linux

2012-03-14 14:30:13

Ubuntu軟件包

2011-09-19 10:15:10

移動界面設計

2020-10-19 19:25:32

Python爬蟲代碼

2021-08-28 11:47:52

json解析

2022-03-22 07:38:00

SQL語句MySQL

2014-05-13 09:55:13

iOS開發工具

2010-10-08 16:32:59

MySQL語句
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 蜜桃视频一区二区三区 | 羞羞色网站 | 精品视频在线免费观看 | 97日韩精品 | 日本小电影网站 | 国产精品 欧美精品 | 国产精品1区2区 | 国产精品久久久久久久久久久久久 | 精品久久久久久亚洲国产800 | 黑人精品欧美一区二区蜜桃 | 爱综合| 色爱综合网 | 国产精品免费观看视频 | 亚洲国产午夜 | 国产精品久久国产精品 | www.4567| 在线观看免费av网站 | 国产电影一区二区在线观看 | 久久国产精彩视频 | 波多野结衣在线观看一区二区三区 | 日韩一区二区在线视频 | 成人精品毛片国产亚洲av十九禁 | 啪啪网页| 欧美成人免费在线视频 | 国内精品免费久久久久软件老师 | 在线视频日韩 | 男人天堂av网站 | 91麻豆精品一区二区三区 | 日韩成人av在线播放 | 黄色中文字幕 | 日韩毛片中文字幕 | 日韩高清国产一区在线 | 日本涩涩网| 狠狠综合久久av一区二区老牛 | 国产韩国精品一区二区三区 | 国产1区| 最新国产视频 | 久久精品视频在线观看 | 国产精品一区二区在线免费观看 | 精品伊人久久 | 中文字字幕一区二区三区四区五区 |