JS - 開發(fā)中的設(shè)計(jì)模式

導(dǎo)讀:

日常開發(fā)中,一些特定的場景下你的處理方法可能并不是很理想,往往這時(shí)借助一些設(shè)計(jì)模式可以讓你優(yōu)雅而高效的實(shí)現(xiàn)這些邏輯,下面就介紹一些雖然不是最全的但一定是最常用的設(shè)計(jì)模式。

單例模式:

定義:一個(gè)類只返回一個(gè)實(shí)例,一旦創(chuàng)建再次調(diào)用就直接返回
使用場景:比如自定義彈窗,無論你程序中多少調(diào)用,都只應(yīng)創(chuàng)建一個(gè)彈窗對(duì)象

class CreateUser {
    constructor(name) {
        this.name = name;
        this.getName();
    }
    getName() {
        return this.name;
    }
};

const ProxyMode = (() => {
    let instance = null;
    return (name) => {
        if(!instance) {
            instance = new CreateUser(name);
        }
        return instance;
    }
})();

let a = ProxyMode('vn');
let b = ProxyMode('lb');

console.log(a, b);   // vn  vn    單例模式只會(huì)創(chuàng)建一次實(shí)例

策略模式:

定義:定義一個(gè)策略類只專注與各方法算法實(shí)現(xiàn),定義一個(gè)接口調(diào)用這些方法
特點(diǎn):代碼優(yōu)雅,可讀性高

// 策略類
const levelObj = {
    "A": money => money * 4,
    "B": money => money * 3,
    "C": money => money * 2
}

// 環(huán)境類  封裝調(diào)用接口
const getMoney = (level, money) => levelObj[level](money);

console.log(getMoney('A', 200))   // 800

代理模式:

定義:為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問
使用場景:比如圖片懶加載,先緩存動(dòng)態(tài) loading,必要時(shí)傳入 src

const imgFunc = (() => {
    let imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc: (src) => {
            imgNode.src = src;
        }
    }
})();

const ProxyImg = (() => {
    let img = new Image();
    img.onload = () => {
        let node = document.getElementsByTagName('img')
        imgFunc.setSrc(img.src);
    }
    return {
        setSrc: (src) => {
            imgFunc.setSrc('../C3photo/jacky/1.jpg');
            img.src = src;
        }
    }
})();

ProxyImg.setSrc('../C3photo/jacky/2.jpg');

裝飾者模式:

定義:裝飾者模式能夠在不改變對(duì)象自身的基礎(chǔ)上,在運(yùn)行程序期間給對(duì)象動(dòng)態(tài)地添加職責(zé)。
使用場景:類似于攔截器,添加對(duì)象的前置和后置事件等。

Function.prototype.before = function(beforefn) {
    let _self = this;                          //保存原函數(shù)引用
    return function(){                         //返回包含了原函數(shù)和新函數(shù)的 '代理函數(shù)'
        beforefn.apply(this, arguments);       //執(zhí)行新函數(shù),修正this
        return _self.apply(this, arguments);   //執(zhí)行原函數(shù)
    }
}
Function.prototype.after = function(afterfn) {
    let _self = this;
    return function(){
        let ret = _self.apply(this, arguments);
        afterfn.apply(this, arguments);
        return ret;
    }
}
let func = function() {
    console.log('2');
}
//func1和func3為掛載函數(shù)
let func1 = function() {
    console.log('1');
}
let func3 = function() {
    console.log('3');
}

func = func.before(func1).after(func3);
func();   // 1  2  3

發(fā)布訂閱模式:

定義:訂閱者(Subscriber)把自己想訂閱的事件注冊(Subscribe)到調(diào)度中心(Event Channel),當(dāng)發(fā)布者(Publisher)發(fā)布該事件(Publish Event)到調(diào)度中心,也就是該事件觸發(fā)時(shí),由調(diào)度中心統(tǒng)一調(diào)度(Fire Event)訂閱者注冊到調(diào)度中心的處理代碼。
使用場景:微信公眾號(hào)的訂閱

let eventEmitter = {
    list: {},                        // 緩存列表(調(diào)度中心)

    on(event, fn) {              // 訂閱
        let _this = this;
        _this.list[event] = _this.list[event] || [];    
        _this.list[event].push(fn);
        return _this;
    },

    emit() {               // 發(fā)布
        let _this = this;
        let event = [].shift.call(arguments),     // shift 會(huì)改變原數(shù)組,因此 arguments 只剩下第二個(gè)參數(shù)
        fns = _this.list[event];
        if(fns && fns.length) {
            fns.forEach(fn => fn.apply(_this, arguments));
        }
        return _this;
    },

    off(event, fn) {                                 // 取消訂閱
        let _this = this;
        let fns = _this.list[event];
        if(!fns) return false;          // 如果緩存列表中沒有相應(yīng)的 fn,返回false
        if(!fn) {
            // 如果沒有傳 fn 的話,就會(huì)將 event 值對(duì)應(yīng)緩存列表中的 fn 都清空
            fns.length = 0;
        } else {
            // 若有 fn,遍歷緩存列表,看看傳入的 fn 與哪個(gè)函數(shù)相同,如果相同就直接從緩存列表中刪掉即可
            for (let i = 0; i < fns.length; i++) {
                if (fns[i] === fn || fns[i].fn === fn) {
                    fns.splice(i, 1);
                    break;
                }
            }
        }
    }
};

const user1 = (content) => {
    console.log('用戶1訂閱了:', content);
}

const user2 = (content) => {
    console.log('用戶2訂閱了:', content);
}

const user3 = (content) => {
    console.log('用戶3訂閱了:', content);
}

// 訂閱
eventEmitter.on('article1', user1);
eventEmitter.on('article1', user2);
eventEmitter.on('article2', user3);

eventEmitter.emit('article1', 'Javascript 發(fā)布-訂閱模式');
eventEmitter.emit('article2', 'Javascript 觀察者模式');

eventEmitter.off('article1', user1);
eventEmitter.emit('article1', 'Javascript 發(fā)布-訂閱模式');


//用戶1訂閱了: Javascript 發(fā)布-訂閱模式
//用戶2訂閱了: Javascript 發(fā)布-訂閱模式
//用戶3訂閱了: Javascript 觀察者模式
//用戶2訂閱了: Javascript 發(fā)布-訂閱模式

總結(jié):

其實(shí)當(dāng)你學(xué)了這么久的前端,做了這么久的項(xiàng)目,回來看看設(shè)計(jì)模式會(huì)發(fā)現(xiàn)它的思想其實(shí)挺有意思的,當(dāng)你試著在工作中寫出這些設(shè)計(jì)模式,你的技術(shù)又會(huì)上一層臺(tái)階啦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • javascript設(shè)計(jì)模式與開發(fā)實(shí)踐 設(shè)計(jì)模式 每個(gè)設(shè)計(jì)模式我們需要從三點(diǎn)問題入手: 定義 作用 用法與實(shí)現(xiàn) 單...
    穿牛仔褲的蚊子閱讀 4,111評(píng)論 0 13
  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡單...
    舟漁行舟閱讀 7,827評(píng)論 2 17
  • 工廠模式 單體模式 模塊模式 代理模式 職責(zé)鏈模式 命令模式 模板方法模式 策略模式 發(fā)布-訂閱模式 中介者模式 ...
    HelloJames閱讀 1,023評(píng)論 0 6
  • 菠蘿范大叔閱讀 574評(píng)論 11 11
  • 產(chǎn)后恢復(fù)到底是恢復(fù)什么? 產(chǎn)后恢復(fù),對(duì)于新媽媽來說,和寶寶的健康成長一樣重要。 產(chǎn)后恢復(fù)的本質(zhì)不僅是媽咪們身材的婀...
    媽媽修復(fù)閱讀 180評(píng)論 0 0