JavaScript--設計模式

一、概述

設計模式是一套被反復使用的、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設計經(jīng)驗的總結(jié)。使用設計模式是為了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。

二、原則

S – Single Responsibility Principle 單一職責原則

  • 一個程序只做好一件事
  • 如果功能過于復雜就拆分開,每個部分保持獨立

O – OpenClosed Principle 開放/封閉原則

  • 對擴展開放,對修改封閉
  • 增加需求時,擴展新代碼,而非修改已有代碼

L – Liskov Substitution Principle 里氏替換原則

  • 子類能覆蓋父類
  • 父類能出現(xiàn)的地方子類就能出現(xiàn)

I – Interface Segregation Principle 接口隔離原則

  • 保持接口的單一獨立
  • 類似單一職責原則,這里更關注接口

D – Dependency Inversion Principle 依賴倒轉(zhuǎn)原則

  • 面向接口編程,依賴于抽象而不依賴于具體
  • 使用方只關注接口而不關注具體類的實現(xiàn)

三、分類

  • 創(chuàng)建型
    1. 單例模式
    2. 原型模式
    3. 工廠模式
    4. 抽象工廠模式
    5. 建造者模式
  • 結(jié)構型
    1. 適配器模式
    2. 裝飾器模式
    3. 代理模式
    4. 外觀模式
    5. 橋接模式
    6. 組合模式
    7. 享元模式
  • 行為型
    1. 觀察者模式
    2. 迭代器模式
    3. 策略模式
    4. 模板方法模式
    5. 職責鏈模式
    6. 命令模式
    7. 備忘錄模式
    8. 狀態(tài)模式
    9. 訪問者模式
    10. 中介模式
    11. 解釋器模式

四、示例

4.1單例模式

概述:

一個類只有一個實例(生成出來對象永遠只有一個實例),并提供一個訪問它的全局訪問點。

實現(xiàn)過程:

  • 構建一個函數(shù)返回一個對象實例
  • 用一個聲明一次變量來控制這個對象實例的生成。
  • 如果該變量里已有一個對象,則直接返回,如果沒有,則生成,生成給變量存起來。

應用:登錄框

閉包實現(xiàn)

function single(){
    let obj //標識
    return function(){
        if(!obj){ //判斷是否為undefined
            obj = new Object()
        }
        return obj
    }
}
let singleObj = single()
let obj1 = singleObj()
let obj2 = singleObj()
console.log(obj1===obj2)//true

原型實現(xiàn)

function singlePrototype(){
    if(!Object.prototype.instance){
        Object.prototype.instance = new Object()
    }
    return Object.prototype.instance
}
let obj1 = singlePrototype()
let obj2 = singlePrototype()
console.log(obj1===obj2) //true

static實現(xiàn)

function singleStatic(){
    if(!Object.instance){
        Object.instance = new Object()
    }
    return Object.instance
}
let obj1 = singleStatic()
let obj2 = singleStatic()
console.log(obj1===obj2)

全局變量實現(xiàn)

function singleWindow(){
    if(!window.instance){
        window.instance = new Object()
    }
    return window.instance
}
let obj1 = singleWindow()
let obj2 = singleWindow()
console.log(obj1===obj2)
4.2工廠模式

概述:

工廠模式生產(chǎn)對象的,以一個工廠方法來生產(chǎn)對應的對象。

實現(xiàn)過程:

  • 手動構建對象
  • 手動給對象設置屬性
  • 手動返回對象
function factory(){
    let obj = new Object()
    obj.name = 'jack'
    return obj
}
4.3組合模式

概述:

將對應多個相同名字方法 放在一個地方統(tǒng)一調(diào)用。

實現(xiàn):

class SayHello{
    constructor(){
        
    }
    say(){
        console.log('hello')
    }
}
class SayHi{
    constructor(){
        
    }
    say(){
        console.log('hi')
    }
}
class SayBay{
    constructor(){
        
    }
    say(){
        console.log('baybay')
    }
}

以上的三個類 分別都具備一個名為say的方法 如果需要調(diào)用的話 那么是一個個的對象進行調(diào)用而不能統(tǒng)一調(diào)用,如果我需要他統(tǒng)一調(diào)用,這個時候我們就可以使用組合模式。

class Combiner{
    constructor(){
        //容器來保存對應的對象
        this.objs = []
    }
    push(obj){
        //添加對象
        this.objs.push(obj)
    }
    excute(fnName){
        //執(zhí)行對應的方法
        this.objs.forEach(item=>{
            item[fnName]()
        })
    }
}
//新建組合模式對象
let combiner = new Combiner()
//傳入對應統(tǒng)一調(diào)用的對象
combiner.push(new SayHello())
combiner.push(new SayHi())
combiner.push(new SayBay())
//執(zhí)行對應的方法
combiner.excute('say')

組合模式在vue中使用

use和install

vue.use()為注冊全局插件所用,接收函數(shù)或者一個包含install屬性的對象為參數(shù),如果參數(shù)帶有install就執(zhí)行install, 如果沒有就直接將參數(shù)當install執(zhí)行, 第一個參數(shù)始終為vue對象, 注冊過的插件不會重新注冊

4.4觀察者模式

概述

  • 觀察者模式(obServer)他又被稱為發(fā)布-訂閱者模式,消息模式等。
  • 定義了一種一對多的關系,讓多個觀察者對象同時監(jiān)聽某一個主題對象,這個主題對象的狀態(tài)發(fā)生變化時就會通知所有的觀察者對象,使它們能夠自動更新自己,當一個對象的改變需要同時改變其它對象,并且它不知道具體有多少對象需要改變的時候,就應該考慮使用觀察者模式。

場景:

  • DOM事件
document.body.addEventListener('click', function() {
    console.log('hello world!');
});
document.body.click()
  • vue響應式

實現(xiàn):

class ObServer{
    constructor(){
        //事件和對應的處理函數(shù)存儲的容器
        this.arg = {} //click:[fn,fn1]
    }
    on(){//發(fā)布事件
    
    }
    emit(){//執(zhí)行處理函數(shù)
        
    }
    off(){//取消事件
    
    }
}

on方法實現(xiàn)

class ObServer{
    constructor(){
        this.arg = {} //{click:[fn,fn1]}
    }
    on(eventName, handler) { //發(fā)布事件 事件名  處理函數(shù)
        if (!this.arg[eventName]) { //沒有這個事件
            this.arg[eventName] = [] //初始化里面為空數(shù)組
        }
        this.arg[eventName].push(handler) //將對應的函數(shù)追加
    }
    emit(){//執(zhí)行處理函數(shù)

    }
    off(){//取消事件

    }
}

emit方法實現(xiàn)

class ObServer{
    constructor(){
        this.arg = {} //{click:[fn,fn1]}
    }
    on(eventName, handler) { //發(fā)布事件 事件名  處理函數(shù)
        if (!this.arg[eventName]) { //沒有這個事件
            this.arg[eventName] = [] //初始化里面為空數(shù)組
        }
        this.arg[eventName].push(handler) //將對應的函數(shù)追加
    }
    emit(eventName, params) { //執(zhí)行處理函數(shù)
        if (!this.arg[eventName]){
            return
        }
        //會將里面的處理函數(shù)都執(zhí)行
        //遍歷對應的處理函數(shù)數(shù)組
        this.arg[eventName].forEach(fn => {
            //將參數(shù)傳入執(zhí)行
            fn.call(this, params)
        })
    }
    off(){//取消事件

    }
}

off方法實現(xiàn)

class ObServer {
    constructor() {
        this.arg = {} //{click:[fn,fn1]}
    }
    on(eventName, handler) { //發(fā)布事件 事件名  處理函數(shù)
        if (!this.arg[eventName]) { //沒有這個事件
            this.arg[eventName] = [] //初始化里面為空數(shù)組
        }
        this.arg[eventName].push(handler) //將對應的函數(shù)追加
    }
    emit(eventName, params) { //執(zhí)行處理函數(shù)
        if (!this.arg[eventName]){
            return
        }
        //會將里面的處理函數(shù)都執(zhí)行
        //遍歷對應的處理函數(shù)數(shù)組
        this.arg[eventName].forEach(fn => {
            //將參數(shù)傳入執(zhí)行
            fn.call(this, params)
        })
    }
    off(eventName, handler) { //取消事件
        if (!this.arg[eventName]) {
            return
        }
        //將這個對應的fn刪除
        if (this.arg[eventName].length == 1) {
            delete this.arg[eventName]
        } else {
            let i
            this.arg[eventName].forEach((item, index) => {
                if (Object.is(item, handler)) {
                    i = index
                }
            })
            this.arg[eventName].splice(i, 1)
        }
    }
}

擴展在觀察者emit方法傳入?yún)?shù) 傳到對應的on里面的處理函數(shù) vue里面子傳父的實現(xiàn)

4.5代理模式

概述:

代理模式利用一個代理對象來處理當前對象事情

假設當A 在心情好的時候收到花,小明表白成功的幾率有60%,而當A 在心情差的時候收到花,小明表白的成功率無限趨近于0。小明跟A 剛剛認識兩天,還無法辨別A 什么時候心情好。如果不合時宜地把花送給A,花被直接扔掉的可能性很大,這束花可是小明吃了7 天泡面換來的。但是A 的朋友B 卻很了解A,所以小明只管把花交給B,B 會監(jiān)聽A 的心情變化,然后選擇A 心情好的時候把花轉(zhuǎn)交給A,

es7新增一個類 Proxy 他就是用于代理的,他是vue3的底層實現(xiàn)

Proxy構造函數(shù)

new Proxy(目標對象,handler處理對象)

對應的處理對象有4大方法

  • get屬性 獲取對應的代理對象的值調(diào)用
  • set屬性 設置代理對象的值調(diào)用
  • deleteProperty 刪除代理對象的屬性調(diào)用
  • has屬性 在遍歷的時候調(diào)用
//目標對象
let target = {name:'張三',age:18,say(){
    console.log('hello');
}}
//利用proxy產(chǎn)生代理對象
let proxy = new Proxy(target,{
    get(target,property,proxy){ //表示目標對象  表示屬性名 表示代理對象
        console.log('get調(diào)用了');
        //訪問值的時候
        if(property =='name'){
            return '我的名字是'+target[property]
        }
        if(property =='age'){
            return '我的年紀是'+target[property]+'歲'
        }
    },
    set(target,property,value){
        //設置值的時候 進行相關操作
        console.log(property);
        console.log(value);
        target[property] = value
    },
    deleteProperty(target,property,proxy){
        //刪除屬性的時候
        console.log('delete調(diào)用了');
        delete target[property]
    },
    has(target,property){
        //in的時候調(diào)用 必須返回boolean 強制轉(zhuǎn)換為boolean類型
        console.log('has調(diào)用了');
        console.log(property);
        return property in target
    },
    apply(target,property){ //函數(shù)調(diào)用觸發(fā)
        console.log('apply調(diào)用了');
    }
})
//讀取代理對象的屬性的時候 會自動調(diào)用get方法 他的值是get方法返回的值
console.log(proxy.age); //調(diào)用get
proxy.name = 'jack' //調(diào)用set
console.log(proxy); 
delete proxy.name //調(diào)用deleteProperty
console.log('name' in proxy); //某個東西是否在某個東西里面返回boolean
console.log(proxy.say); //代理只第一層

apply對應的方法

function sum(a, b) {
    return a + b;
}

const handler = {
    apply: function (target, thisArg, argumentsList) { //目標對象 當前this 參數(shù)數(shù)組
        console.log('apply調(diào)用了');
        // expected output: "Calculate sum: 1,2"

        return target(argumentsList[0], argumentsList[1]) * 10;
    }
};

const proxy1 = new Proxy(sum, handler);

console.log(sum(1, 2));
// expected output: 3
console.log(proxy1(1, 2));
// expected output: 30
4.6裝飾者模式

概述:

  • 動態(tài)地給某個對象添加一些額外的職責,,是一種實現(xiàn)繼承的替代方案
  • 在不改變原對象的基礎上,通過對其進行包裝擴展,使原有對象可以滿足用戶的更復雜需求,而不會影響從這個類中派生的其他對象

實現(xiàn):

//原本類
class Car{
    constructor(){
        
    }
    run(){
        console.log('車在跑')
    }
}
//增強的類
class Decorater{
    constructor(car){
        this.car = car
    }
    run(){
        console.log('我邊吃飯邊開車')
        this.car.run()
    }
}
new Decorater(new Car()).run()

擴展:es7新增一個裝飾器 其實就是裝飾器模式的封裝

4.7適配器模式

概述

將一個類的接口轉(zhuǎn)化為另外一個接口,以滿足用戶需求,使類之間接口不兼容問題通過適配器得以解決。

實現(xiàn):

let phone = {
    fn(){
        retrun '5v'
    }
}
class Target{
    constructor(){
    }
    fn(){
        let v = phone.fn()
        return '220轉(zhuǎn)換為'+v
    }
}
new Target().fn()

應用:

  • 整合第三方SDK
  • 封裝舊接口
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • ## **JavaScript設計模式** ## **設計模式簡介** 設計模式代表了最佳的實踐,通常被有經(jīng)驗的面...
    你別太自信閱讀 220評論 0 0
  • 概念 設計模式是一套被反復使用的、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設計經(jīng)驗的總結(jié)。使用設計模式是為了重用代碼、讓...
    Moon_f3e1閱讀 4,068評論 0 0
  • 1. 面向?qū)ο?1.1 封裝 封裝的目的在于將信息隱藏。廣義的封裝不僅包括封裝數(shù)據(jù)和封裝實現(xiàn),還包括封裝類型和封裝...
    nimw閱讀 666評論 0 1
  • 編程設計模式 六大基本原則 總原則:開閉原則(Open Close Principle,OCP) 開:對拓展開放,...
    月明星稀_8184閱讀 482評論 0 0
  • 設計模式簡介 設計模式代表了最佳的實踐,通常被有經(jīng)驗的面向?qū)ο蟮能浖_發(fā)人員所采用。設計模式是軟件開發(fā)人員在軟件開...
    _嗯_哼_閱讀 495評論 0 0

友情鏈接更多精彩內(nèi)容