以前看過《JavaScript設計模式與開發實踐》這本書,對書中的設計模式和一些相關案例也有了必定的瞭解,同時把這些設計模式的應用對應在在一些其餘的項目中,進行了一些整理,以下僅供參考:javascript
補充:若是如下內容有什麼不對的地方,歡迎指正。html
設計模式是爲了更好的代碼重用性,可讀性,可靠性,可維護性。前端
1)單一職責原則
2)里氏替換原則
3)依賴倒轉原則
4)接口隔離原則
5)最少知識原則(迪米特法則)
6)開放封閉原則
java
整體來講設計模式分爲三大類:git
建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。github
結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。設計模式
行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。數組
其實還有兩類:併發型模式和線程池模式。瀏覽器
不過,對於前端來講,有的設計模式在平時工做中幾乎用不到或者不多用到,來來來,來了解下前端常見的設計模式併發
常見設計模式:
一、工廠模式
常見的實例化對象模式,工廠模式就至關於建立實例對象的new,提供一個建立對象的接口
// 某個須要建立的具體對象
class Product {
constructor (name) {
this.name = name;
}
init () {}
}
// 工廠對象
class Creator {
create (name) {
return new Product(name);
}
}
const creator = new Creator();
const p = creator.create(); // 經過工廠對象建立出來的具體對象
複製代碼
應用場景:JQuery中的$、Vue.component異步組件、React.createElement等
二、單例模式
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點,通常登陸、購物車等都是一個單例。
// 單例對象
class SingleObject {
login () {}
}
// 訪問方法
SingleObject.getInstance = (function () {
let instance;
return function () {
if (!instance) {
instance = new SingleObject();
}
return instance;
}
})()
const obj1 = SingleObject.getInstance();
const obj2 = SingleObject.getInstance();
console.log(obj1 === obj2); // true
複製代碼
應用場景:JQuery中的$、Vuex中的Store、Redux中的Store等
三、適配器模式
用來解決兩個接口不兼容問題,由一個對象來包裝不兼容的對象,好比參數轉換,容許直接訪問
class Adapter {
specificRequest () {
return '德國標準插頭';
}
}
// 適配器對象,對原來不兼容對象進行包裝處理
class Target {
constructor () {
this.adapter = new Adapter();
}
request () {
const info = this.adapter.specificRequest();
console.log(`${info} - 轉換器 - 中國標準插頭`)
}
}
const target = new Target();
console.log(target.request()); // 德國標準插頭 - 轉換器 - 中國標準插頭
複製代碼
應用場景:Vue的computed、舊的JSON格式轉換成新的格式等
四、裝飾器模式
在不改變對象自身的基礎上,動態的給某個對象添加新的功能,同時又不改變其接口
class Plane {
fire () {
console.log('發送普通子彈');
}
}
// 裝飾過的對象
class Missile {
constructor (plane) {
this.plane = plane;
}
fire () {
this.plane.fire();
console.log('發射導彈');
}
}
let plane = new Plane();
plane = new Missile(plane);
console.log(plane.fire()); // 依次打印 發送普通子彈 發射導彈
複製代碼
利用AOP給函數動態添加功能,即Function的after或者before
Function.prototype.before = function (beforeFn) {
const _self = this;
return function () {
beforeFn.apply(this, arguments);
return _self.apply(this, arguments);
}
}
Function.prototype.after = function (afterFn) {
const _self = this;
return function () {
const ret = _self.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
}
}
let func = function () {
console.log('2');
}
func = func.before(function() {
console.log('1');
}).after(function() {
console.log('3');
})
func();
console.log(func()); // 依次打印 1 2 3
複製代碼
應用場景:ES7裝飾器、Vuex中1.0版本混入Vue時,重寫init方法、Vue中數組變異方法實現等
五、代理模式
爲其餘對象提供一種代理,便以控制對這個對象的訪問,不能直接訪問目標對象
class Flower {}
// 源對象
class Jack {
constructor (target) {
this.target = target;
}
sendFlower (target) {
const flower = new Flower();
this.target.receiveFlower(flower)
}
}
// 目標對象
class Rose {
receiveFlower (flower) {
console.log('收到花: ' + flower)
}
}
// 代理對象
class ProxyObj {
constructor () {
this.target = new Rose();
}
receiveFlower (flower) {
this.sendFlower(flower)
}
sendFlower (flower) {
this.target.receiveFlower(flower)
}
}
const proxyObj = new ProxyObj();
const jack = new Jack(proxyObj);
jack.sendFlower(proxyObj); // 收到花:[object Object]
複製代碼
應用場景:ES6 Proxy、Vuex中對於getters訪問、圖片預加載等
六、外觀模式
爲一組複雜的子系統接口提供一個更高級的統一接口,經過這個接口使得對子系統接口的訪問更容易,不符合單一職責原則和開放封閉原則
class A {
eat () {}
}
class B {
eat () {}
}
class C {
eat () {
const a = new A();
const b = new B();
a.eat();
b.eat();
}
}
// 跨瀏覽器事件偵聽器
function addEvent(el, type, fn) {
if (window.addEventListener) {
el.addEventListener(type, fn, false);
} else if (window.attachEvent) {
el.attachEvent('on' + type, fn);
} else {
el['on' + type] = fn;
}
}
複製代碼
應用場景:JS事件不一樣瀏覽器兼容處理、同一方法能夠傳入不一樣參數兼容處理等
七、觀察者模式
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,全部依賴於它的對象都將獲得通知
class Subject {
constructor () {
this.state = 0;
this.observers = [];
}
getState () {
return this.state;
}
setState (state) {
this.state = state;
this.notify();
}
notify () {
this.observers.forEach(observer => {
observer.update();
})
}
attach (observer) {
this.observers.push(observer);
}
}
class Observer {
constructor (name, subject) {
this.name = name;
this.subject = subject;
this.subject.attach(this);
}
update () {
console.log(`${this.name} update, state: ${this.subject.getState()}`);
}
}
let sub = new Subject();
let observer1 = new Observer('o1', sub);
let observer2 = new Observer('o2', sub);
sub.setState(1);
複製代碼
觀察者模式與發佈/訂閱模式區別: 本質上的區別是調度的地方不一樣
雖然兩種模式都存在訂閱者和發佈者(具體觀察者可認爲是訂閱者、具體目標可認爲是發佈者),可是觀察者模式是由具體目標調度的,而發佈/訂閱模式是統一由調度中心調的,因此觀察者模式的訂閱者與發佈者之間是存在依賴的,而發佈/訂閱模式則不會。
---觀察者模式:目標和觀察者是基類,目標提供維護觀察者的一系列方法,觀察者提供更新接口。具體觀察者和具體目標繼承各自的基類,而後具體觀察者把本身註冊到具體目標裏,在具體目標發生變化時候,調度觀察者的更新方法。
好比有個「天氣中心」的具體目標A,專門監聽天氣變化,而有個顯示天氣的界面的觀察者B,B就把本身註冊到A裏,當A觸發天氣變化,就調度B的更新方法,並帶上本身的上下文。
---發佈/訂閱模式:訂閱者把本身想訂閱的事件註冊到調度中心,當該事件觸發時候,發佈者發佈該事件到調度中心(順帶上下文),由調度中心統一調度訂閱者註冊到調度中心的處理代碼。
好比有個界面是實時顯示天氣,它就訂閱天氣事件(註冊到調度中心,包括處理程序),當天氣變化時(定時獲取數據),就做爲發佈者發佈天氣信息到調度中心,調度中心就調度訂閱者的天氣處理程序。
應用場景:JS事件、JS Promise、JQuery.$CallBack、Vue watch、NodeJS自定義事件,文件流等
八、迭代器模式
提供一種方法順序訪問一個聚合對象中各個元素, 而又無須暴露該對象的內部表示
可分爲:內部迭代器和外部迭代器
內部迭代器: 內部已經定義好迭代規則,外部只須要調用一次便可。
const each = (args, fn) => {
for (let i = 0, len = args.length; i < len; i++) {
const value = fn(args[i], i, args);
if (value === false) break;
}
}
複製代碼
應用場景: JQuery.each方法
外部迭代器:必須顯示的請求迭代下一個元素。
// 迭代器
class Iterator {
constructor (list) {
this.list = list;
this.index = 0;
}
next () {
if (this.hasNext()) {
return this.list[this.index++]
}
return null;
}
hasNext () {
if (this.index === this.list.length) {
return false;
}
return true;
}
}
const arr = [1, 2, 3, 4, 5, 6];
const ite = new Iterator();
while(ite.hasNext()) {
console.log(ite.next()); // 依次打印 1 2 3 4 5 6
}
複製代碼
應用場景:JS Iterator、JS Generator
九、狀態模式
關鍵是區分事物內部的狀態,事物內部狀態每每會帶來事物的行爲改變,即容許對象在內部狀態發生改變時改變它的行爲
// 紅燈
class RedLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to red light');
this.state.setState(this.state.greenLight)
}
}
// 綠燈
class greenLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to green light');
this.state.setState(this.state.yellowLight)
}
}
// 黃燈
class yellowLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to yellow light');
this.state.setState(this.state.redLight)
}
}
class State {
constructor () {
this.redLight = new RedLight(this)
this.greenLight = new greenLight(this)
this.yellowLight = new yellowLight(this)
this.setState(this.redLight) // 初始化爲紅燈
}
setState (state) {
this.currState = state;
}
}
const state = new State();
state.currState.light() // turn to red light
setInterval(() => {
state.currState.light() // 每隔3秒依次打印紅燈、綠燈、黃燈
}, 3000)
複製代碼
應用場景:燈泡狀態、紅綠燈切換等
其餘設計模式:
十、命令模式
十一、組合模式
十二、享元模式
1三、策略模式
1四、職責鏈模式
1五、模板方法模式
1六、中介者模式
1七、備忘錄模式
1八、訪問者模式
1九、解釋器模式
20、橋接模式
其餘設計模式請移步:github.com/jefferyE
更多設計模式,具體請參考:www.runoob.com