前端常用的設計模式
轉載自:https://zhuanlan.zhihu.com/p/115874575
一、常用設計模式
1、單例模式:確保只有一個例項,並提供全域性訪問。
2、策略模式:定義一些列的演算法,把它們一個個封裝起來,並且使它們可以相互替換。
3、代理模式:為一個物件提供一個代用品或佔位符,以便控制對它的訪問。js裡虛擬代理(網路請求方面)、快取代理(資料方面)最常用
4、迭代器模式:提供一種方法,順序訪問一個聚合物件中的各個元素,而又不需要暴露該物件的內部表示。不需要關心物件的內部構造,也可以按順序訪問其中的每個元素。很多語言都有自己內建的迭代器,比如js的Array.prototype.forEach
5、釋出-訂閱模式:又叫
var a={} a.b={} a.c=function(key,fn){ if(!this.b[key]){ this.b[key]=[] } this.b[key].push(fn) } a.d=function(){ var key =Array.prototype.shift.call(arguments), fns=this.b[key] console.log(arguments) if(!fns||fns.length===0){ return false; } for(var i=0,fn;fn=fns[i++];){ fn.apply(this,arguments) } } a.c('88',function(pri){ console.log(pri) }) a.c('110',function(pri){ console.log(pri) }) a.d('88',200) a.d('110',300)
6、命令模式:執行某些特定事情的指令。記錄資訊的清單,就是命令模式中的命令物件。
PS:有時候,需要像某些物件傳送請求,但是並不知道請求的接收者是誰,也不知道被請求的操作是什麼。這時候就需要命令模式,使得請求傳送者和請求接收者能夠消除耦合關係。
7、組合模式:使用樹形方式建立物件的結構,把相同的操作應用在組合物件和單個物件上。
8、模板方法模式:只需要整合就可以實現,由兩部分組成,第一部分是抽象類,第二部分是具體的實現子類。通常在抽象父類中封裝了子類的演算法框架,包括實現一些公共方法以及封裝子類中所有方法的執行順序。子類通過繼承這個抽象類,也繼承了整個演算法結構,並且可以選擇重寫父類的方法。
//抽象類 var a=function(){}; a.prototype.b=function(){ console.log('把水煮沸') } a.prototype.c=function(){} a.prototype.d=function(){} a.prototype.e=function(){} a.prototype.init=function(){ this.b(); this.c(); this.d(); this.e(); } //子類 var g=function(){} g.prototype.c=function(){ console.log('用沸水衝咖啡') } g.prototype.d=function(){ console.log('把咖啡倒進杯子') } g.prototype.e=function(){ console.log('加糖和牛奶') } var h=new g() h.init()
這裡的a.prototype.init就是模板方法
9、好萊塢原則:即高層元件呼叫底層元件,模板方法是好萊塢的一個典型使用場景;子類放棄了對自己的控制權,而是改為父類通知子類哪些方法應該在什麼時候被呼叫。作為子類,只負責提供一些設計上的細節。
Ps:釋出-訂閱模式、回撥函式都用到了此原則
10、享元模式:運用共享技術來有效支援大量細粒度的物件。
Ps:
- 內部狀態儲存與物件內部。
- 內部狀態可以被一些物件共享。
- 內部狀態獨立於具體的場景,通常不會改變。
- 外部狀態取決於具體的場景,並根據場景而變化,外部狀態不能被共享。
11、物件池:維護一個裝在空閒物件的池子,如果需要物件的時候,不是直接new,而是轉從物件池裡獲取。如果物件池裡沒有空閒物件,則建立一個新的物件,當獲取出的物件完成它的職責後,再進入池子等待被下次獲取;
12、職責鏈模式:使多個物件都有機會處理請求,並從而避免請求的傳送者和接收者之間的耦合關係,將這些物件連成一條鏈,並沿著這條鏈傳遞該請求,直到有一個物件處理它為止。
PS:有多個if-else條件的時候,要去考慮是否可以用職責鏈模式。
13、中介者模式:解除物件與物件之間得耦合關係。增加一個中介者物件後,所有得相關物件都通過中介者物件來通訊,而不是互相引用,所有當一個物件發生改變時,只需要通知中介者物件即可。此模式迎合迪米特法則的一種實現,迪米特也叫做最少知識原則,是指一個物件應該儘可能少地瞭解另外的物件。
PS:編寫思路:1、利用釋出-訂閱模式2、在中介者物件中開放一些接收訊息得介面
14、裝飾者模式:給物件動態地增加職責。這種方式並沒有真正地改動物件自身,而是將物件放入另一個物件之中,這些物件以一條鏈的方式進行引用,形成一個聚合物件。
PS:經常用到Function.pototype.after和Function.pototype.before兩個函式進行裝飾
Function.prototype.before=function(beforefn){ var _self=this;//儲存原函式引用 return function(){// 返回包含了原函式和新函式的“代理”函式 beforefn.apply(this,arguments);// 執行新函式,且保證this不被劫持,新函式接受的引數也會被原封不動地傳入原函式,新函式在原函式之前執行 return _self.apply(this,arguments);// 執行原函式並返回原函式的執行結果,並且保證this不被劫持 } }
注意:這裡的beforefn和原函式_self共用一組引數列表arguments,當我們在beforefn的函式體內改變arguments的時候,_self接收的引數列表自然也會變化,所以經常用於動態地改變原函式的引數
15、狀態模式:關鍵是區分事物內部的狀態,事物內部狀態的改變往往會帶來事物的行為改變。此模式關鍵是把事物的每種狀態都封裝成單獨的類。
PS:狀態模式和策略模式區別:
策略模式的各個策略類之間是平等又平行的,他們之間沒有任何聯絡,所以客戶必須熟知這些策略類的作用。以便隨時切換演算法;而狀態模式,狀態和狀態對應的行為早已被封裝好的,狀態之間的切換也早被規定完成,“改變行為”這件事發生在狀態模式內部。對客戶來說,並不需要了解這些細節,這正是狀態模式的作用所在。
16、介面卡模式:解決兩個軟體實體的介面不相容的問題。不考慮介面是怎樣實現的,也不考慮將來可能會如何讓變化,介面卡模式不需要改變已有的介面,就能夠使他們協同作用,介面卡模式通常只包裝一次。
var a={ show:function(){ console.log('a資料來源開始渲染') } } var b={ disply:function(){ console.log('b資料來源開始渲染') } } //介面卡c var c={ show:function(){ return b.disply(); } } var render=function(fn){ fn.show() } render(a) render(c)
17、外觀模式:在JS中不常用。主要是為子系統中的一組介面提供一個一致的介面,定義了一個高層的介面,這個介面使子系統更加容易使用。在C++或者JAVA中指的是一組類的集合,這些類相互協作可以組成系統中一個相對獨立的部分。但在JS中,這個子系統至少應該指的是一組函式的集合。
var A=function(){ a1() a2() }