JavaScript--設(shè)計模式

一谈截、概述

設(shè)計模式是一套被反復(fù)使用的筷屡、多數(shù)人知曉的、經(jīng)過分類編目的簸喂、代碼設(shè)計經(jīng)驗(yàn)的總結(jié)毙死。使用設(shè)計模式是為了重用代碼、讓代碼更容易被他人理解喻鳄、保證代碼可靠性扼倘。

二、原則

S – Single Responsibility Principle 單一職責(zé)原則

  • 一個程序只做好一件事
  • 如果功能過于復(fù)雜就拆分開除呵,每個部分保持獨(dú)立

O – OpenClosed Principle 開放/封閉原則

  • 對擴(kuò)展開放唉锌,對修改封閉
  • 增加需求時,擴(kuò)展新代碼竿奏,而非修改已有代碼

L – Liskov Substitution Principle 里氏替換原則

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

I – Interface Segregation Principle 接口隔離原則

  • 保持接口的單一獨(dú)立
  • 類似單一職責(zé)原則,這里更關(guān)注接口

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

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

三泛啸、分類

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

四、示例

4.1單例模式

概述:

一個類只有一個實(shí)例(生成出來對象永遠(yuǎn)只有一個實(shí)例)秃症,并提供一個訪問它的全局訪問點(diǎn)候址。

實(shí)現(xiàn)過程:

  • 構(gòu)建一個函數(shù)返回一個對象實(shí)例
  • 用一個聲明一次變量來控制這個對象實(shí)例的生成吕粹。
  • 如果該變量里已有一個對象,則直接返回岗仑,如果沒有匹耕,則生成,生成給變量存起來荠雕。

應(yīng)用:登錄框

閉包實(shí)現(xiàn)

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

原型實(shí)現(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實(shí)現(xiàn)

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

全局變量實(shí)現(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)對應(yīng)的對象。

實(shí)現(xiàn)過程:

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

概述:

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

實(shí)現(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)用的話 那么是一個個的對象進(jìn)行調(diào)用而不能統(tǒng)一調(diào)用既鞠,如果我需要他統(tǒng)一調(diào)用,這個時候我們就可以使用組合模式盖文。

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

組合模式在vue中使用

use和install

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

4.4觀察者模式

概述

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

場景:

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

實(shí)現(xiàn):

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

on方法實(shí)現(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) //將對應(yīng)的函數(shù)追加
    }
    emit(){//執(zhí)行處理函數(shù)

    }
    off(){//取消事件

    }
}

emit方法實(shí)現(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) //將對應(yīng)的函數(shù)追加
    }
    emit(eventName, params) { //執(zhí)行處理函數(shù)
        if (!this.arg[eventName]){
            return
        }
        //會將里面的處理函數(shù)都執(zhí)行
        //遍歷對應(yīng)的處理函數(shù)數(shù)組
        this.arg[eventName].forEach(fn => {
            //將參數(shù)傳入執(zhí)行
            fn.call(this, params)
        })
    }
    off(){//取消事件

    }
}

off方法實(shí)現(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) //將對應(yīng)的函數(shù)追加
    }
    emit(eventName, params) { //執(zhí)行處理函數(shù)
        if (!this.arg[eventName]){
            return
        }
        //會將里面的處理函數(shù)都執(zhí)行
        //遍歷對應(yīng)的處理函數(shù)數(shù)組
        this.arg[eventName].forEach(fn => {
            //將參數(shù)傳入執(zhí)行
            fn.call(this, params)
        })
    }
    off(eventName, handler) { //取消事件
        if (!this.arg[eventName]) {
            return
        }
        //將這個對應(yīng)的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)
        }
    }
}

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

4.5代理模式

概述:

代理模式利用一個代理對象來處理當(dāng)前對象事情

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

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

Proxy構(gòu)造函數(shù)

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

對應(yīng)的處理對象有4大方法

  • get屬性 獲取對應(yīng)的代理對象的值調(diào)用
  • set屬性 設(shè)置代理對象的值調(diào)用
  • deleteProperty 刪除代理對象的屬性調(diào)用
  • has屬性 在遍歷的時候調(diào)用
//目標(biāo)對象
let target = {name:'張三',age:18,say(){
    console.log('hello');
}}
//利用proxy產(chǎn)生代理對象
let proxy = new Proxy(target,{
    get(target,property,proxy){ //表示目標(biāo)對象  表示屬性名 表示代理對象
        console.log('get調(diào)用了');
        //訪問值的時候
        if(property =='name'){
            return '我的名字是'+target[property]
        }
        if(property =='age'){
            return '我的年紀(jì)是'+target[property]+'歲'
        }
    },
    set(target,property,value){
        //設(shè)置值的時候 進(jìn)行相關(guān)操作
        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 強(qiáng)制轉(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對應(yīng)的方法

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

const handler = {
    apply: function (target, thisArg, argumentsList) { //目標(biāo)對象 當(dāng)前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)地給某個對象添加一些額外的職責(zé),,是一種實(shí)現(xiàn)繼承的替代方案
  • 在不改變原對象的基礎(chǔ)上瓦糕,通過對其進(jìn)行包裝擴(kuò)展底洗,使原有對象可以滿足用戶的更復(fù)雜需求,而不會影響從這個類中派生的其他對象

實(shí)現(xiàn):

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

擴(kuò)展:es7新增一個裝飾器 其實(shí)就是裝飾器模式的封裝

4.7適配器模式

概述

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

實(shí)現(xiàn):

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

應(yīng)用:

  • 整合第三方SDK
  • 封裝舊接口
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末圣勒,一起剝皮案震驚了整個濱河市费变,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌灾而,老刑警劉巖胡控,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異旁趟,居然都是意外死亡昼激,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門锡搜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來橙困,“玉大人,你說我怎么就攤上這事耕餐》哺担” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵肠缔,是天一觀的道長夏跷。 經(jīng)常有香客問我,道長明未,這世上最難降的妖魔是什么槽华? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮趟妥,結(jié)果婚禮上猫态,老公的妹妹穿的比我還像新娘。我一直安慰自己披摄,他們只是感情好亲雪,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著疚膊,像睡著了一般义辕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寓盗,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天终息,我揣著相機(jī)與錄音夺巩,去河邊找鬼。 笑死周崭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的喳张。 我是一名探鬼主播续镇,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼销部!你這毒婦竟也來了摸航?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤舅桩,失蹤者是張志新(化名)和其女友劉穎酱虎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體擂涛,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡读串,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了撒妈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恢暖。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖狰右,靈堂內(nèi)的尸體忽然破棺而出杰捂,到底是詐尸還是另有隱情,我是刑警寧澤棋蚌,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布嫁佳,位于F島的核電站,受9級特大地震影響谷暮,放射性物質(zhì)發(fā)生泄漏蒿往。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一坷备、第九天 我趴在偏房一處隱蔽的房頂上張望熄浓。 院中可真熱鬧,春花似錦省撑、人聲如沸赌蔑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽娃惯。三九已至,卻和暖如春肥败,著一層夾襖步出監(jiān)牢的瞬間趾浅,已是汗流浹背愕提。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留皿哨,地道東北人浅侨。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像证膨,于是被迫代替她去往敵國和親如输。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內(nèi)容

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