成人怡红院-成人怡红院视频在线观看-成人影视大全-成人影院203nnxyz-美女毛片在线看-美女免费黄

站長資訊網
最全最豐富的資訊網站

javascript中的設計模式有哪些

javascript中的設計模式有:單例模式、策略模式、代理模式、迭代器模式、“發布-訂閱”模式、命令模式、組合模式、模板方法模式、享元模式、職責鏈模式、中介者模式、裝飾者模式、狀態模式、適配器模式、外觀模式等。

javascript中的設計模式有哪些

本教程操作環境:windows7系統、javascript1.8.5版、Dell G3電腦。

JavaScript中常見的十五種設計模式

一、單例模式

1. 定義

保證一個類僅有一個實例,并提供一個訪問它的全局訪問點

2. 核心

確保只有一個實例,并提供全局訪問

3. 實現

假設要設置一個管理員,多次調用也僅設置一次,我們可以使用閉包緩存一個內部變量來實現這個單例

function SetManager(name) {     this.manager = name; }  SetManager.prototype.getName = function() {     console.log(this.manager); };  var SingletonSetManager = (function() {     var manager = null;      return function(name) {         if (!manager) {             manager = new SetManager(name);         }          return manager;     }  })();  SingletonSetManager('a').getName(); // a SingletonSetManager('b').getName(); // a SingletonSetManager('c').getName(); // a

這是比較簡單的做法,但是假如我們還要設置一個HR呢?就得復制一遍代碼了

所以,可以改寫單例內部,實現地更通用一些

// 提取出通用的單例 function getSingleton(fn) {     var instance = null;      return function() {         if (!instance) {             instance = fn.apply(this, arguments);         }          return instance;     } }

再進行調用,結果還是一樣

// 獲取單例 var managerSingleton = getSingleton(function(name) {     var manager = new SetManager(name);     return manager; });  managerSingleton('a').getName(); // a managerSingleton('b').getName(); // a managerSingleton('c').getName(); // a

這時,我們添加HR時,就不需要更改獲取單例內部的實現了,僅需要實現添加HR所需要做的,再調用即可

function SetHr(name) {     this.hr = name; }  SetHr.prototype.getName = function() {     console.log(this.hr); };  var hrSingleton = getSingleton(function(name) {     var hr = new SetHr(name);     return hr; });  hrSingleton('aa').getName(); // aa hrSingleton('bb').getName(); // aa hrSingleton('cc').getName(); // aa

或者,僅想要創建一個p層,不需要將對象實例化,直接調用函數

結果為頁面中僅有第一個創建的p

function createPopup(html) {     var div = document.createElement('div');     div.innerHTML = html;     document.body.append(div);      return div; }  var popupSingleton = getSingleton(function() {     var div = createPopup.apply(this, arguments);     return div; });  console.log(     popupSingleton('aaa').innerHTML,     popupSingleton('bbb').innerHTML,     popupSingleton('bbb').innerHTML ); // aaa  aaa  aaa

二、策略模式

1. 定義

定義一系列的算法,把它們一個個封裝起來,并且使它們可以相互替換。

2. 核心

將算法的使用和算法的實現分離開來。

一個基于策略模式的程序至少由兩部分組成:

第一個部分是一組策略類,策略類封裝了具體的算法,并負責具體的計算過程。

第二個部分是環境類Context,Context接受客戶的請求,隨后把請求委托給某一個策略類。要做到這點,說明Context 中要維持對某個策略對象的引用

3. 實現

策略模式可以用于組合一系列算法,也可用于組合一系列業務規則

假設需要通過成績等級來計算學生的最終得分,每個成績等級有對應的加權值。我們可以利用對象字面量的形式直接定義這個組策略

// 加權映射關系 var levelMap = {     S: 10,     A: 8,     B: 6,     C: 4 };  // 組策略 var scoreLevel = {     basicScore: 80,      S: function() {         return this.basicScore + levelMap['S'];      },      A: function() {         return this.basicScore + levelMap['A'];      },      B: function() {         return this.basicScore + levelMap['B'];      },      C: function() {         return this.basicScore + levelMap['C'];      } }  // 調用 function getScore(level) {     return scoreLevel[level] ? scoreLevel[level]() : 0; }  console.log(     getScore('S'),     getScore('A'),     getScore('B'),     getScore('C'),     getScore('D') ); // 90 88 86 84 0

在組合業務規則方面,比較經典的是表單的驗證方法。這里列出比較關鍵的部分

// 錯誤提示 var errorMsgs = {     default: '輸入數據格式不正確',     minLength: '輸入數據長度不足',     isNumber: '請輸入數字',     required: '內容不為空' };  // 規則集 var rules = {     minLength: function(value, length, errorMsg) {         if (value.length < length) {             return errorMsg || errorMsgs['minLength']         }     },     isNumber: function(value, errorMsg) {         if (!/d+/.test(value)) {             return errorMsg || errorMsgs['isNumber'];         }     },     required: function(value, errorMsg) {         if (value === '') {             return errorMsg || errorMsgs['required'];         }     } };  // 校驗器 function Validator() {     this.items = []; };  Validator.prototype = {     constructor: Validator,          // 添加校驗規則     add: function(value, rule, errorMsg) {         var arg = [value];          if (rule.indexOf('minLength') !== -1) {             var temp = rule.split(':');             arg.push(temp[1]);             rule = temp[0];         }          arg.push(errorMsg);          this.items.push(function() {             // 進行校驗             return rules[rule].apply(this, arg);         });     },          // 開始校驗     start: function() {         for (var i = 0; i < this.items.length; ++i) {             var ret = this.items[i]();                          if (ret) {                 console.log(ret);                 // return ret;             }         }     } };  // 測試數據 function testTel(val) {     return val; }  var validate = new Validator();  validate.add(testTel('ccc'), 'isNumber', '只能為數字'); // 只能為數字 validate.add(testTel(''), 'required'); // 內容不為空 validate.add(testTel('123'), 'minLength:5', '最少5位'); // 最少5位 validate.add(testTel('12345'), 'minLength:5', '最少5位');  var ret = validate.start();  console.log(ret);

4. 優缺點

優點

可以有效地避免多重條件語句,將一系列方法封裝起來也更直觀,利于維護

缺點

往往策略集會比較多,我們需要事先就了解定義好所有的情況

三、代理模式

1. 定義

為一個對象提供一個代用品或占位符,以便控制對它的訪問

2. 核心

當客戶不方便直接訪問一個 對象或者不滿足需要的時候,提供一個替身對象 來控制對這個對象的訪問,客戶實際上訪問的是 替身對象。

替身對象對請求做出一些處理之后, 再把請求轉交給本體對象

代理和本體的接口具有一致性,本體定義了關鍵功能,而代理是提供或拒絕對它的訪問,或者在訪問本體之前做一 些額外的事情

3. 實現

代理模式主要有三種:保護代理、虛擬代理、緩存代理

保護代理主要實現了訪問主體的限制行為,以過濾字符作為簡單的例子

// 主體,發送消息 function sendMsg(msg) {     console.log(msg); }  // 代理,對消息進行過濾 function proxySendMsg(msg) {     // 無消息則直接返回     if (typeof msg === 'undefined') {         console.log('deny');         return;     }          // 有消息則進行過濾     msg = ('' + msg).replace(/泥s*煤/g, '');      sendMsg(msg); }   sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀 proxySendMsg('泥煤呀泥 煤'); // 呀 proxySendMsg(); // deny

它的意圖很明顯,在訪問主體之前進行控制,沒有消息的時候直接在代理中返回了,拒絕訪問主體,這數據保護代理的形式

有消息的時候對敏感字符進行了處理,這屬于虛擬代理的模式

虛擬代理在控制對主體的訪問時,加入了一些額外的操作

在滾動事件觸發的時候,也許不需要頻繁觸發,我們可以引入函數節流,這是一種虛擬代理的實現

// 函數防抖,頻繁操作中不處理,直到操作完成之后(再過 delay 的時間)才一次性處理 function debounce(fn, delay) {     delay = delay || 200;          var timer = null;      return function() {         var arg = arguments;                    // 每次操作時,清除上次的定時器         clearTimeout(timer);         timer = null;                  // 定義新的定時器,一段時間后進行操作         timer = setTimeout(function() {             fn.apply(this, arg);         }, delay);     } };  var count = 0;  // 主體 function scrollHandle(e) {     console.log(e.type, ++count); // scroll }  // 代理 var proxyScrollHandle = (function() {     return debounce(scrollHandle, 500); })();  window.onscroll = proxyScrollHandle;

緩存代理可以為一些開銷大的運算結果提供暫時的緩存,提升效率

來個栗子,緩存加法操作

// 主體 function add() {     var arg = [].slice.call(arguments);      return arg.reduce(function(a, b) {         return a + b;     }); }  // 代理 var proxyAdd = (function() {     var cache = [];      return function() {         var arg = [].slice.call(arguments).join(',');                  // 如果有,則直接從緩存返回         if (cache[arg]) {             return cache[arg];         } else {             var ret = add.apply(this, arguments);             return ret;         }     }; })();  console.log(     add(1, 2, 3, 4),     add(1, 2, 3, 4),      proxyAdd(10, 20, 30, 40),     proxyAdd(10, 20, 30, 40) ); // 10 10 100 100

四、迭代器模式

1. 定義

迭代器模式是指提供一種方法順序訪問一個聚合對象中的各個元素,而又不需要暴露該對象的內部表示。

2. 核心

在使用迭代器模式之后,即使不關心對象的內部構造,也可以按順序訪問其中的每個元素

3. 實現

JS中數組的map forEach 已經內置了迭代器

[1, 2, 3].forEach(function(item, index, arr) {     console.log(item, index, arr); });

不過對于對象的遍歷,往往不能與數組一樣使用同一的遍歷代碼

我們可以封裝一下

function each(obj, cb) {     var value;      if (Array.isArray(obj)) {         for (var i = 0; i < obj.length; ++i) {             value = cb.call(obj[i], i, obj[i]);              if (value === false) {                 break;             }         }     } else {         for (var i in obj) {             value = cb.call(obj[i], i, obj[i]);              if (value === false) {                 break;             }         }     } }  each([1, 2, 3], function(index, value) {     console.log(index, value); });  each({a: 1, b: 2}, function(index, value) {     console.log(index, value); });  // 0 1 // 1 2 // 2 3  // a 1 // b 2

再來看一個例子,強行地使用迭代器,來了解一下迭代器也可以替換頻繁的條件語句

雖然例子不太好,但在其他負責的分支判斷情況下,也是值得考慮的

function getManager() {     var year = new Date().getFullYear();      if (year <= 2000) {         console.log('A');     } else if (year >= 2100) {         console.log('C');     } else {         console.log('B');     } }  getManager(); // B

將每個條件語句拆分出邏輯函數,放入迭代器中迭代

function year2000() {     var year = new Date().getFullYear();      if (year <= 2000) {         console.log('A');     }      return false; }  function year2100() {     var year = new Date().getFullYear();      if (year >= 2100) {         console.log('C');     }      return false; }  function year() {     var year = new Date().getFullYear();      if (year > 2000 && year < 2100) {         console.log('B');     }      return false; }  function iteratorYear() {     for (var i = 0; i < arguments.length; ++i) {         var ret = arguments[i]();          if (ret !== false) {             return ret;         }     } }  var manager = iteratorYear(year2000, year2100, year); // B

五、發布-訂閱模式

1. 定義

也稱作觀察者模式,定義了對象間的一種一對多的依賴關系,當一個對象的狀態發 生改變時,所有依賴于它的對象都將得到通知

2. 核心

取代對象之間硬編碼的通知機制,一個對象不用再顯式地調用另外一個對象的某個接口。

與傳統的發布-訂閱模式實現方式(將訂閱者自身當成引用傳入發布者)不同,在JS中通常使用注冊回調函數的形式來訂閱

3. 實現

JS中的事件就是經典的發布-訂閱模式的實現

// 訂閱 document.body.addEventListener('click', function() {     console.log('click1'); }, false);  document.body.addEventListener('click', function() {     console.log('click2'); }, false);  // 發布 document.body.click(); // click1  click2

自己實現一下

小A在公司C完成了筆試及面試,小B也在公司C完成了筆試。他們焦急地等待結果,每隔半天就電話詢問公司C,導致公司C很不耐煩。

一種解決辦法是 AB直接把聯系方式留給C,有結果的話C自然會通知AB

這里的“詢問”屬于顯示調用,“留給”屬于訂閱,“通知”屬于發布

// 觀察者 var observer = {     // 訂閱集合     subscribes: [],      // 訂閱     subscribe: function(type, fn) {         if (!this.subscribes[type]) {             this.subscribes[type] = [];         }                  // 收集訂閱者的處理         typeof fn === 'function' && this.subscribes[type].push(fn);     },      // 發布  可能會攜帶一些信息發布出去     publish: function() {         var type = [].shift.call(arguments),             fns = this.subscribes[type];                  // 不存在的訂閱類型,以及訂閱時未傳入處理回調的         if (!fns || !fns.length) {             return;         }                  // 挨個處理調用         for (var i = 0; i < fns.length; ++i) {             fns[i].apply(this, arguments);         }     },          // 刪除訂閱     remove: function(type, fn) {         // 刪除全部         if (typeof type === 'undefined') {             this.subscribes = [];             return;         }          var fns = this.subscribes[type];          // 不存在的訂閱類型,以及訂閱時未傳入處理回調的         if (!fns || !fns.length) {             return;         }          if (typeof fn === 'undefined') {             fns.length = 0;             return;         }          // 挨個處理刪除         for (var i = 0; i < fns.length; ++i) {             if (fns[i] === fn) {                 fns.splice(i, 1);             }         }     } };  // 訂閱崗位列表 function jobListForA(jobs) {     console.log('A', jobs); }  function jobListForB(jobs) {     console.log('B', jobs); }  // A訂閱了筆試成績 observer.subscribe('job', jobListForA); // B訂閱了筆試成績 observer.subscribe('job', jobListForB);   // A訂閱了筆試成績 observer.subscribe('examinationA', function(score) {     console.log(score); });  // B訂閱了筆試成績 observer.subscribe('examinationB', function(score) {     console.log(score); });  // A訂閱了面試結果 observer.subscribe('interviewA', function(result) {     console.log(result); });  observer.publish('examinationA', 100); // 100 observer.publish('examinationB', 80); // 80 observer.publish('interviewA', '備用'); // 備用  observer.publish('job', ['前端', '后端', '測試']); // 輸出A和B的崗位   // B取消訂閱了筆試成績 observer.remove('examinationB'); // A都取消訂閱了崗位 observer.remove('job', jobListForA);  observer.publish('examinationB', 80); // 沒有可匹配的訂閱,無輸出 observer.publish('job', ['前端', '后端', '測試']); // 輸出B的崗位

4. 優缺點

優點

一為時間上的解耦,二為對象之間的解耦。可以用在異步編程中與MV*框架中

缺點

創建訂閱者本身要消耗一定的時間和內存,訂閱的處理函數不一定會被執行,駐留內存有性能開銷

弱化了對象之間的聯系,復雜的情況下可能會導致程序難以跟蹤維護和理解

六、命令模式

1. 定義

用一種松耦合的方式來設計程序,使得請求發送者和請求接收者能夠消除彼此之間的耦合關系

命令(command)指的是一個執行某些特定事情的指令

2. 核心

命令中帶有execute執行、undo撤銷、redo重做等相關命令方法,建議顯示地指示這些方法名

3. 實現

簡單的命令模式實現可以直接使用對象字面量的形式定義一個命令

var incrementCommand = {     execute: function() {         // something     } };

不過接下來的例子是一個自增命令,提供執行、撤銷、重做功能

采用對象創建處理的方式,定義這個自增

// 自增 function IncrementCommand() {     // 當前值     this.val = 0;     // 命令棧     this.stack = [];     // 棧指針位置     this.stackPosition = -1; };  IncrementCommand.prototype = {     constructor: IncrementCommand,      // 執行     execute: function() {         this._clearRedo();                  // 定義執行的處理         var command = function() {             this.val += 2;         }.bind(this);                  // 執行并緩存起來         command();                  this.stack.push(command);          this.stackPosition++;          this.getValue();     },          canUndo: function() {         return this.stackPosition >= 0;     },          canRedo: function() {         return this.stackPosition < this.stack.length - 1;     },      // 撤銷     undo: function() {         if (!this.canUndo()) {             return;         }                  this.stackPosition--;          // 命令的撤銷,與執行的處理相反         var command = function() {             this.val -= 2;         }.bind(this);                  // 撤銷后不需要緩存         command();          this.getValue();     },          // 重做     redo: function() {         if (!this.canRedo()) {             return;         }                  // 執行棧頂的命令         this.stack[++this.stackPosition]();          this.getValue();     },          // 在執行時,已經撤銷的部分不能再重做     _clearRedo: function() {         this.stack = this.stack.slice(0, this.stackPosition + 1);     },          // 獲取當前值     getValue: function() {         console.log(this.val);     } };

再實例化進行測試,模擬執行、撤銷、重做的操作

var incrementCommand = new IncrementCommand();  // 模擬事件觸發,執行命令 var eventTrigger = {     // 某個事件的處理中,直接調用命令的處理方法     increment: function() {         incrementCommand.execute();     },      incrementUndo: function() {         incrementCommand.undo();     },      incrementRedo: function() {         incrementCommand.redo();     } };   eventTrigger['increment'](); // 2 eventTrigger['increment'](); // 4  eventTrigger['incrementUndo'](); // 2  eventTrigger['increment'](); // 4  eventTrigger['incrementUndo'](); // 2 eventTrigger['incrementUndo'](); // 0 eventTrigger['incrementUndo'](); // 無輸出  eventTrigger['incrementRedo'](); // 2 eventTrigger['incrementRedo'](); // 4 eventTrigger['incrementRedo'](); // 無輸出  eventTrigger['increment'](); // 6

此外,還可以實現簡單的宏命令(一系列命令的集合)

var MacroCommand = {     commands: [],      add: function(command) {         this.commands.push(command);          return this;     },      remove: function(command) {         if (!command) {             this.commands = [];             return;         }          for (var i = 0; i < this.commands.length; ++i) {             if (this.commands[i] === command) {                 this.commands.splice(i, 1);             }         }     },      execute: function() {         for (var i = 0; i < this.commands.length; ++i) {             this.commands[i].execute();         }     } };  var showTime = {     execute: function() {         console.log('time');     } };  var showName = {     execute: function() {         console.log('name');     } };  var showAge = {     execute: function() {         console.log('age');     } };  MacroCommand.add(showTime).add(showName).add(showAge);  MacroCommand.remove(showName);  MacroCommand.execute(); // time age

七、組合模式

1. 定義

是用小的子對象來構建更大的 對象,而這些小的子對象本身也許是由更小 的“孫對象”構成的。

2. 核心

可以用樹形結構來表示這種“部分- 整體”的層次結構。

調用組合對象 的execute方法,程序會遞歸調用組合對象 下面的葉對象的execute方法

javascript中的設計模式有哪些

但要注意的是,組合模式不是父子關系,它是一種HAS-A(聚合)的關系,將請求委托給 它所包含的所有葉對象。基于這種委托,就需要保證組合對象和葉對象擁有相同的 接口

此外,也要保證用一致的方式對待 列表中的每個葉對象,即葉對象屬于同一類,不需要過多特殊的額外操作

3. 實現

使用組合模式來實現掃描文件夾中的文件

// 文件夾 組合對象 function Folder(name) {     this.name = name;     this.parent = null;     this.files = []; }  Folder.prototype = {     constructor: Folder,      add: function(file) {         file.parent = this;         this.files.push(file);          return this;     },      scan: function() {         // 委托給葉對象處理         for (var i = 0; i < this.files.length; ++i) {             this.files[i].scan();         }     },      remove: function(file) {         if (typeof file === 'undefined') {             this.files = [];             return;         }          for (var i = 0; i < this.files.length; ++i) {             if (this.files[i] === file) {                 this.files.splice(i, 1);             }         }     } };  // 文件 葉對象 function File(name) {     this.name = name;     this.parent = null; }  File.prototype = {     constructor: File,      add: function() {         console.log('文件里面不能添加文件');     },      scan: function() {         var name = [this.name];         var parent = this.parent;          while (parent) {             name.unshift(parent.name);             parent = parent.parent;         }          console.log(name.join(' / '));     } };

構造好組合對象與葉對象的關系后,實例化,在組合對象中插入組合或葉對象

var web = new Folder('Web'); var fe = new Folder('前端'); var css = new Folder('CSS'); var js = new Folder('js'); var rd = new Folder('后端');  web.add(fe).add(rd);  var file1 = new File('HTML權威指南.pdf'); var file2 = new File('CSS權威指南.pdf'); var file3 = new File('JavaScript權威指南.pdf'); var file4 = new File('MySQL基礎.pdf'); var file5 = new File('Web安全.pdf'); var file6 = new File('Linux菜鳥.pdf');  css.add(file2); fe.add(file1).add(file3).add(css).add(js); rd.add(file4).add(file5); web.add(file6);  rd.remove(file4);  // 掃描 web.scan();

掃描結果為

javascript中的設計模式有哪些

4. 優缺點

優點

可 以方便地構造一棵樹來表示對象的部分-整體 結構。在樹的構造最終 完成之后,只需要通過請求樹的最頂層對 象,便能對整棵樹做統一一致的操作。

缺點

創建出來的對象長得都差不多,可能會使代碼不好理解,創建太多的對象對性能也會有一些影響

八、模板方法模式

1. 定義

模板方法模式由兩部分結構組成,第一部分是抽象父類,第二部分是具體的實現子類。

2. 核心

在抽象父類中封裝子類的算法框架,它的 init方法可作為一個算法的模板,指導子類以何種順序去執行哪些方法。

由父類分離出公共部分,要求子類重寫某些父類的(易變化的)抽象方法

3. 實現

模板方法模式一般的實現方式為繼承

以運動作為例子,運動有比較通用的一些處理,這部分可以抽離開來,在父類中實現。具體某項運動的特殊性則有自類來重寫實現。

最終子類直接調用父類的模板函數來執行

// 體育運動 function Sport() {  }  Sport.prototype = {     constructor: Sport,          // 模板,按順序執行     init: function() {         this.stretch();         this.jog();         this.deepBreath();         this.start();          var free = this.end();                  // 運動后還有空的話,就拉伸一下         if (free !== false) {             this.stretch();         }              },          // 拉伸     stretch: function() {         console.log('拉伸');     },          // 慢跑     jog: function() {         console.log('慢跑');     },          // 深呼吸     deepBreath: function() {         console.log('深呼吸');     },      // 開始運動     start: function() {         throw new Error('子類必須重寫此方法');     },      // 結束運動     end: function() {         console.log('運動結束');     } };  // 籃球 function Basketball() {  }  Basketball.prototype = new Sport();  // 重寫相關的方法 Basketball.prototype.start = function() {     console.log('先投上幾個三分'); };  Basketball.prototype.end = function() {     console.log('運動結束了,有事先走一步');     return false; };   // 馬拉松 function Marathon() {  }  Marathon.prototype = new Sport();  var basketball = new Basketball(); var marathon = new Marathon();  // 子類調用,最終會按照父類定義的順序執行 basketball.init(); marathon.init();

javascript中的設計模式有哪些

九、享元模式

1. 定義

享元(flyweight)模式是一種用于性能優化的模式,它的目標是盡量減少共享對象的數量

2. 核心

運用共享技術來有效支持大量細粒度的對象。

強調將對象的屬性劃分為內部狀態(屬性)與外部狀態(屬性)。內部狀態用于對象的共享,通常不變;而外部狀態則剝離開來,由具體的場景決定。

3. 實現

在程序中使用了大量的相似對象時,可以利用享元模式來優化,減少對象的數量

舉個栗子,要對某個班進行身體素質測量,僅測量身高體重來評判

// 健康測量 function Fitness(name, sex, age, height, weight) {     this.name = name;     this.sex = sex;     this.age = age;     this.height = height;     this.weight = weight; }  // 開始評判 Fitness.prototype.judge = function() {     var ret = this.name + ': ';      if (this.sex === 'male') {         ret += this.judgeMale();     } else {         ret += this.judgeFemale();     }      console.log(ret); };  // 男性評判規則 Fitness.prototype.judgeMale = function() {     var ratio = this.height / this.weight;      return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8); };  // 女性評判規則 Fitness.prototype.judgeFemale = function() {     var ratio = this.height / this.weight;          return this.age > 20 ? (ratio > 4) : (ratio > 3); };   var a = new Fitness('A', 'male', 18, 160, 80); var b = new Fitness('B', 'male', 21, 180, 70); var c = new Fitness('C', 'female', 28, 160, 80); var d = new Fitness('D', 'male', 18, 170, 60); var e = new Fitness('E', 'female', 18, 160, 40);  // 開始評判 a.judge(); // A: false b.judge(); // B: false c.judge(); // C: false d.judge(); // D: true e.judge(); // E: true

評判五個人就需要創建五個對象,一個班就幾十個對象

可以將對象的公共部分(內部狀態)抽離出來,與外部狀態獨立。將性別看做內部狀態即可,其他屬性都屬于外部狀態。

這么一來我們只需要維護男和女兩個對象(使用factory對象),而其他變化的部分則在外部維護(使用manager對象)

// 健康測量 function Fitness(sex) {     this.sex = sex; }  // 工廠,創建可共享的對象 var FitnessFactory = {     objs: [],      create: function(sex) {         if (!this.objs[sex]) {             this.objs[sex] = new Fitness(sex);         }          return this.objs[sex];     } };  // 管理器,管理非共享的部分 var FitnessManager = {     fitnessData: {},          // 添加一項     add: function(name, sex, age, height, weight) {         var fitness = FitnessFactory.create(sex);                  // 存儲變化的數據         this.fitnessData[name] = {             age: age,             height: height,             weight: weight         };          return fitness;     },          // 從存儲的數據中獲取,更新至當前正在使用的對象     updateFitnessData: function(name, obj) {         var fitnessData = this.fitnessData[name];          for (var item in fitnessData) {             if (fitnessData.hasOwnProperty(item)) {                 obj[item] = fitnessData[item];             }         }     } };  // 開始評判 Fitness.prototype.judge = function(name) {     // 操作前先更新當前狀態(從外部狀態管理器中獲取)     FitnessManager.updateFitnessData(name, this);      var ret = name + ': ';      if (this.sex === 'male') {         ret += this.judgeMale();     } else {         ret += this.judgeFemale();     }      console.log(ret); };  // 男性評判規則 Fitness.prototype.judgeMale = function() {     var ratio = this.height / this.weight;      return this.age > 20 ? (ratio > 3.5) : (ratio > 2.8); };  // 女性評判規則 Fitness.prototype.judgeFemale = function() {     var ratio = this.height / this.weight;          return this.age > 20 ? (ratio > 4) : (ratio > 3); };   var a = FitnessManager.add('A', 'male', 18, 160, 80); var b = FitnessManager.add('B', 'male', 21, 180, 70); var c = FitnessManager.add('C', 'female', 28, 160, 80); var d = FitnessManager.add('D', 'male', 18, 170, 60); var e = FitnessManager.add('E', 'female', 18, 160, 40);  // 開始評判 a.judge('A'); // A: false b.judge('B'); // B: false c.judge('C'); // C: false d.judge('D'); // D: true e.judge('E'); // E: true

不過代碼可能更復雜了,這個例子可能還不夠充分,只是展示了享元模式如何實現,它節省了多個相似的對象,但多了一些操作。

factory對象有點像單例模式,只是多了一個sex的參數,如果沒有內部狀態,則沒有參數的factory對象就更接近單例模式了

十、職責鏈模式

1. 定義

使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關系,將這些對象連成一條鏈,并沿著這條鏈 傳遞該請求,直到有一個對象處理它為止

2. 核心

請求發送者只需要知道鏈中的第一個節點,弱化發送者和一組接收者之間的強聯系,可以便捷地在職責鏈中增加或刪除一個節點,同樣地,指定誰是第一個節點也很便捷

3. 實現

以展示不同類型的變量為例,設置一條職責鏈,可以免去多重if條件分支

// 定義鏈的某一項 function ChainItem(fn) {     this.fn = fn;     this.next = null; }  ChainItem.prototype = {     constructor: ChainItem,          // 設置下一項     setNext: function(next) {         this.next = next;         return next;     },          // 開始執行     start: function() {         this.fn.apply(this, arguments);     },          // 轉到鏈的下一項執行     toNext: function() {         if (this.next) {             this.start.apply(this.next, arguments);         } else {             console.log('無匹配的執行項目');         }     } };  // 展示數字 function showNumber(num) {     if (typeof num === 'number') {         console.log('number', num);     } else {         // 轉移到下一項         this.toNext(num);     } }  // 展示字符串 function showString(str) {     if (typeof str === 'string') {         console.log('string', str);     } else {         this.toNext(str);     } }  // 展示對象 function showObject(obj) {     if (typeof obj === 'object') {         console.log('object', obj);     } else {         this.toNext(obj);     } }  var chainNumber = new ChainItem(showNumber); var chainString = new ChainItem(showString); var chainObject = new ChainItem(showObject);  // 設置鏈條 chainObject.setNext(chainNumber).setNext(chainString);  chainString.start('12'); // string 12 chainNumber.start({}); // 無匹配的執行項目 chainObject.start({}); // object {} chainObject.start(123); // number 123

這時想判斷未定義的時候呢,直接加到鏈中即可

// 展示未定義 function showUndefined(obj) {     if (typeof obj === 'undefined') {         console.log('undefined');     } else {         this.toNext(obj);     } }  var chainUndefined = new ChainItem(showUndefined); chainString.setNext(chainUndefined);  chainNumber.start(); // undefined

由例子可以看到,使用了職責鏈后,由原本的條件分支換成了很多對象,雖然結構更加清晰了,但在一定程度上可能會影響到性能,所以要注意避免過長的職責鏈。

十一、中介者模式

1. 定義

所有的相關 對象都通過中介者對象來通信,而不是互相引用,所以當一個對象發生改變時,只需要通知中介者對象即可

2. 核心

使網狀的多對多關系變成了相對簡單的一對多關系(復雜的調度處理都交給中介者)

javascript中的設計模式有哪些

使用中介者后

javascript中的設計模式有哪些

3. 實現

多個對象,指的不一定得是實例化的對象,也可以將其理解成互為獨立的多個項。當這些項在處理時,需要知曉并通過其他項的數據來處理。

如果每個項都直接處理,程序會非常復雜,修改某個地方就得在多個項內部修改

我們將這個處理過程抽離出來,封裝成中介者來處理,各項需要處理時,通知中介者即可。

var A = {     score: 10,      changeTo: function(score) {         this.score = score;          // 自己獲取         this.getRank();     },          // 直接獲取     getRank: function() {         var scores = [this.score, B.score, C.score].sort(function(a, b) {             return a < b;         });          console.log(scores.indexOf(this.score) + 1);     } };  var B = {     score: 20,      changeTo: function(score) {         this.score = score;          // 通過中介者獲取         rankMediator(B);     } };  var C = {     score: 30,      changeTo: function(score) {         this.score = score;          rankMediator(C);     } };  // 中介者,計算排名 function rankMediator(person) {     var scores = [A.score, B.score, C.score].sort(function(a, b) {         return a < b;     });      console.log(scores.indexOf(person.score) + 1); }  // A通過自身來處理 A.changeTo(100); // 1  // B和C交由中介者處理 B.changeTo(200); // 1 C.changeTo(50); // 3

ABC三個人分數改變后想要知道自己的排名,在A中自己處理,而B和C使用了中介者。B和C將更為輕松,整體代碼也更簡潔

最后,雖然中介者做到了對模塊和對象的解耦,但有時對象之間的關系并非一定要解耦,強行使用中介者來整合,可能會使代碼更為繁瑣,需要注意。

十二、裝飾者模式

1. 定義

以動態地給某個對象添加一些額外的職責,而不會影響從這個類中派生的其他對象。

是一種“即用即付”的方式,能夠在不改變對 象自身的基礎上,在程序運行期間給對象動態地 添加職責

2. 核心

是為對象動態加入行為,經過多重包裝,可以形成一條裝飾鏈

3. 實現

最簡單的裝飾者,就是重寫對象的屬性

var A = {     score: 10 };  A.score = '分數:' + A.score;

可以使用傳統面向對象的方法來實現裝飾,添加技能

function Person() {}  Person.prototype.skill = function() {     console.log('數學'); };  // 裝飾器,還會音樂 function MusicDecorator(person) {     this.person = person; }  MusicDecorator.prototype.skill = function() {     this.person.skill();     console.log('音樂'); };  // 裝飾器,還會跑步 function RunDecorator(person) {     this.person = person; }  RunDecorator.prototype.skill = function() {     this.person.skill();     console.log('跑步'); };  var person = new Person();  // 裝飾一下 var person1 = new MusicDecorator(person); person1 = new RunDecorator(person1);  person.skill(); // 數學 person1.skill(); // 數學 音樂 跑步

在JS中,函數為一等對象,所以我們也可以使用更通用的裝飾函數

// 裝飾器,在當前函數執行前先執行另一個函數 function decoratorBefore(fn, beforeFn) {     return function() {         var ret = beforeFn.apply(this, arguments);                  // 在前一個函數中判斷,不需要執行當前函數         if (ret !== false) {             fn.apply(this, arguments);         }     }; }   function skill() {     console.log('數學'); }  function skillMusic() {     console.log('音樂'); }  function skillRun() {     console.log('跑步'); }  var skillDecorator = decoratorBefore(skill, skillMusic); skillDecorator = decoratorBefore(skillDecorator, skillRun);  skillDecorator(); // 跑步 音樂 數學

十三、狀態模式

1. 定義

事物內部狀態的改變往往會帶來事物的行為改變。在處理的時候,將這個處理委托給當前的狀態對象即可,該狀態對象會負責渲染它自身的行為

2. 核心

區分事物內部的狀態,把事物的每種狀態都封裝成單獨的類,跟此種狀態有關的行為都被封裝在這個類的內部

3. 實現

以一個人的工作狀態作為例子,在剛醒、精神、疲倦幾個狀態中切換著

// 工作狀態 function Work(name) {     this.name = name;     this.currentState = null;      // 工作狀態,保存為對應狀態對象     this.wakeUpState = new WakeUpState(this);     // 精神飽滿     this.energeticState = new EnergeticState(this);     // 疲倦     this.tiredState = new TiredState(this);      this.init(); }  Work.prototype.init = function() {     this.currentState = this.wakeUpState;          // 點擊事件,用于觸發更新狀態     document.body.onclick = () => {         this.currentState.behaviour();     }; };  // 更新工作狀態 Work.prototype.setState = function(state) {     this.currentState = state; }  // 剛醒 function WakeUpState(work) {     this.work = work; }  // 剛醒的行為 WakeUpState.prototype.behaviour = function() {     console.log(this.work.name, ':', '剛醒呢,睡個懶覺先');          // 只睡了2秒鐘懶覺就精神了..     setTimeout(() => {         this.work.setState(this.work.energeticState);     }, 2 * 1000); }  // 精神飽滿 function EnergeticState(work) {     this.work = work; }  EnergeticState.prototype.behaviour = function() {     console.log(this.work.name, ':', '超級精神的');          // 才精神1秒鐘就發困了     setTimeout(() => {         this.work.setState(this.work.tiredState);     }, 1000); };  // 疲倦 function TiredState(work) {     this.work = work; }  TiredState.prototype.behaviour = function() {     console.log(this.work.name, ':', '怎么肥事,好困');          // 不知不覺,又變成了剛醒著的狀態... 不斷循環呀     setTimeout(() => {         this.work.setState(this.work.wakeUpState);     }, 1000); };   var work = new Work('曹操');

點擊一下頁面,觸發更新狀態的操作

javascript中的設計模式有哪些

4. 優缺點

優點

狀態切換的邏輯分布在狀態類中,易于維護

缺點

多個狀態類,對于性能來說,也是一個缺點,這個缺點可以使用享元模式來做進一步優化

將邏輯分散在狀態類中,可能不會很輕易就能看出狀態機的變化邏輯

十四、適配器模式

1. 定義

是解決兩個軟件實體間的接口不兼容的問題,對不兼容的部分進行適配

2. 核心

解決兩個已有接口之間不匹配的問題

3. 實現

比如一個簡單的數據格式轉換的適配器

// 渲染數據,格式限制為數組了 function renderData(data) {     data.forEach(function(item) {         console.log(item);     }); }  // 對非數組的進行轉換適配 function arrayAdapter(data) {     if (typeof data !== 'object') {         return [];     }      if (Object.prototype.toString.call(data) === '[object Array]') {         return data;     }      var temp = [];      for (var item in data) {         if (data.hasOwnProperty(item)) {             temp.push(data[item]);         }     }      return temp; }  var data = {     0: 'A',     1: 'B',     2: 'C' };  renderData(arrayAdapter(data)); // A B C

十五、外觀模式

1. 定義

為子系統中的一組接口提供一個一致的界面,定義一個高層接口,這個接口使子系統更加容易使用

2. 核心

可以通過請求外觀接口來達到訪問子系統,也可以選擇越過外觀來直接訪問子系統

3. 實現

外觀模式在JS中,可以認為是一組函數的集合

// 三個處理函數 function start() {     console.log('start'); }  function doing() {     console.log('doing'); }  function end() {     console.log('end'); }  // 外觀函數,將一些處理統一起來,方便調用 function execute() {     start();     doing();     end(); }   // 調用init開始執行 function init() {     // 此處直接調用了高層函數,也可以選擇越過它直接調用相關的函數     execute(); }  init(); // start doing end

贊(0)
分享到: 更多 (0)
?
網站地圖   滬ICP備18035694號-2    滬公網安備31011702889846號
美女又大又黄WWW免费网站| 欧美人与动XXXXZ0OZ| 嫩BBB槡BBBB搡BBBB| 人妻巨大乳一二三区麻豆| 色一情一乱一伦一区二区三区| 玩弄三个高大的熟妇| 亚洲ⅤA中文字幕无码毛片| 亚洲码与欧洲码一二三四区别| 用各种刑具调教吹潮的视频| 99国产精品久久久久久久成人热| MM1313亚洲精品无码| 高清偷自拍亚洲精品三区| 国精产品W灬源码1688网站 | 被按摩的人妻中文字幕BD| 国产JIZZJIZZ麻豆全部免| 国语做受对白XXXXX在线| 乱码精品一区二区三区| 人人妻人人妻人人人人妻妻人人人人| 无码人妻AⅤ一区二区三区用会员| 亚洲精品国产第一综合99久久| 中文字幕久久波多野结衣AV| 成人用品有限公司| 狠狠色丁香久久综合婷婷| 免费久久人人爽人人爽AV| 日韩一区二区无码视频| 亚洲成AV人综合在线观看| 777米奇第四在线观看| 国产AV无码专区亚洲AV中文| 九九精品99久久久香蕉| 区产品乱码芒果精品P站| 午夜成人爽爽爽视频在线观看| 一本久道久久综合狠狠老| 被黑人下药做得受不了| 国产怡春院无码一区二区| 蜜桃久久久久久精品免费观看| 色婷婷久久久SWAG精品| 亚洲色精品VR一区二区三区 | 青梅被从小摸到大H补课1视频| 午夜成人理论无码电影在线播放| 中文在线ずっと好きだった| 国产成人精品免费视频网页大全| 久久久久99精品成人片三人毛片| 人妻体内射精一区二区| 亚洲AV自慰白浆喷水网站| CHINESE偷拍宾馆TUBE| 国产主播AV福利精品一区| 女性裸体无遮挡啪啪网站| 无人区码卡二卡乱码字幕| 中文字幕天天躁日日躁狠狠躁免费 | 色情ⅩXXX欧美色妇HD| 亚洲熟妇丰满XXXXX国语| 成人精品一区二区三区电影| 精品亚洲国产成人AV在线| 日韩AV一卡2卡3卡4卡新区乱| 亚洲人成电影网站色WWW| 顶级欧美熟妇高潮XXXXX| 久久精品一区二区三区中文字幕| 日韩少妇激情一区二区| 一本大道久久A久久综合| 国产AV无码专区亚洲AV麻豆丫 | 999国产精品999久久久久久| 国产重口老太和两个小伙另类| 秋霞午夜无码鲁丝片午夜| 亚洲国产精品无码久久98| 成 人免费 在线手机版视| 久久午夜夜伦鲁鲁片免费无码影院| 天天爽夜夜爽夜夜爽| 中文字幕精品亚洲无线码一区应用 | 午夜私人电影院在线观看| А√天堂资源中文在线官网| 久久不见久久见中文字幕免费 | 国产精品久久香蕉免费播放| 欧美男生射精高潮视频网站| 亚洲人成人无码WWW| 国产成人亚洲精品无码青| 欧美乱大交XXXXX在线观看| 亚洲乱色熟女一区二区三区蜜臀| 国产成人精品亚洲精品| 青草伊人久久综在合线亚洲观看| 夜夜精品无码一区二区三区| 国产色视频网站免费| 日韩精品极品视频在线观看免费| 中国女人FREE性HD| 精品成在人线AV无码免费看| 婷婷成人丁香五月综合激情| 阿娇13分钟视频无删减MP4| 久久午夜无码鲁丝片| 亚洲АV天堂手机版在线观看| 国产69精品久久久久成人小说| 欧美国产一区二区三区激情无套| 亚洲综合国产一区二区三区| 国产性自爱拍偷在在线播放| 丝瓜草莓视频APP| 把腿张开老子臊烂你的动漫| 蜜桃AV自慰久久久久免费网站| 亚洲鲁丝片一区二区三区| 国产欧美日韩专区发布| 少妇夜夜春夜夜爽试看视频| Z〇ZOZ〇女人另类ZOZ〇| 免费国产成人AⅤ观看| 亚洲自偷自拍熟女另类| 激情 小说 亚洲 图片 伦| 无码中文人妻在线一区二区三区| 嗯啊开小嫩苞好深啊H视频| 人妻少妇粗大持久满足| 99无人区码一码二码三码...| 久久婷婷五月综合色和啪| 亚洲精选无码久久久| 好嗨哟片在线观看| 亚洲 都市 校园 激情 另类| 国产精品WWW夜色视频| 首页 动漫 亚洲 欧美 日韩| 成熟妇女一区av| 日本适合18岁以上的护肤品| 啊轻点内射在线视频| 欧美成人午夜视频| 777米奇色888狠狠俺去啦| 两个病娇男友轮流爱我资源 | 欧美成人一区二区三区在线视频 | 亚洲伊人伊成久久人综合网| 久久99成人精一区二区三区 | 麻豆传煤入口免费进入2023| 一本一道精品欧美中文字幕| 久久精品免费一区二区三区| 亚洲人成无码网站| 久久精品人妻一区二区三区| 亚洲无人区码一二三四区别| 国产精品白浆无码流出视频| 人物动物交互狗AA| Y111111少妇影院无码| 黑人巨大精品欧美一区二区免费 | 欧美老人巨大XXXX做受视频 | 欧美性爱视频一二三区| HD2LINODE日本成熟IP| 人妻丰满AV中文久久不卡| 扒开腿挺进湿润的花苞HD视频| 日本亲与子乱人妻HD| 粉嫩性色一区二区三区AV| 双乳被一左一右的吸着| 国产精品久久久久久免费软件| 羞羞麻豆国产精品1区2区3区| 好爽又高潮了毛片| 亚洲色大18成人网站WWW在线 | 夜夜爽夜夜叫夜夜高潮漏水| 里番ACG ※里番_ACG工口| 最新国产精品久久精品| 欧美日韩精品一区二区三区不卡 | 忘忧草在线播放WWW网| 国产无遮挡又黄又爽在线视频| 亚洲AV无码国产精品麻豆天美| 精品亚洲AⅤ无码一区二区三区| 夜夜躁天天躁很很躁| 欧美极品少妇做受| 粉嫩av一区二区三区| 性色AV无码中文AV有码VR| 久久狠狠色噜噜狠狠狠狠97| √天堂资源中文WWW| 日本XXXXXXXXX69| 国产精品国产三级欧美二区| 亚洲精品国产A久久久久久| 乱人伦XXXX国语对白| 把她按在桌上疯狂顶撞| 偷偷色噜狠狠狠狠的777米奇| 交换朋友夫妻客厅互换4韩国| 在线观看免费AV网| 人妻夜夜爽天天爽| 国产麻花豆剧传媒精品MV| 亚洲午夜性春猛交XXXX偷窥| 女人高潮娇喘抽搐喷水动态视频| 成人无码区免费A∨| 亚洲AV成人永久网站www在线| 久久天天躁夜夜躁狠狠I女人| YY111111少妇无码影院| 无码人妻精品一区二区三区99不 | 铜铜铜铜铜铜铜铜好大好深色| 护士被医生办公室狂玩| 最新亚洲春色AV无码专区| 色婷婷AV一区二区三区在线观看| 国语对白露脸XXXXXX| 中文字幕在线不卡精品视频99| 色欲av成人蜜臀av不卡| 精东传媒VS天美传媒在线| 99国产精品久久久蜜芽 | 日本适合18岁以上的护肤品| 果冻传媒影视在线播放| 2020国产精品久久久久精品| 天堂VA视频一区二区| 久久精品无码专区免费东京热| 啊轻点都日出水来了| 亚洲AV色香蕉一区二区三区夜夜嗨| 免费看漫画在线成人漫画| 国产成人免费A在线视频| 野花 高清 中文 免费 日本 | 夫妻之间的100种插秧法| 亚洲精品无码久久久久久小说| 欧美一区二区三区性视频野战 | 亚洲AV永久无码精品无码一区二区 | 亚洲伊人久久大香线蕉| 日本免费AⅤ欧美在线观看| 精品亚洲成A人在线观看青青 |