javascript設計模式及應用場景

前言

這裏花幾分鐘時間來說解幾個設計模式以及它們的應用場景。能夠幫助你在平時開發時把設計模式用起來html

  • 裝飾器模式
  • 迭代器模式
  • 發佈訂閱模式
  • 工廠模式
  • 單例模式
  • 代理模式
  • 外觀模式

廢話很少說,直接上demo前端

裝飾器模式

裝飾器DEMO
<!--index.js-->
function decoratorHoc(target){
    target.customerName = 'xuqiang';
}

@decoratorHoc
class Person{}

console.log(Person.customerName);
複製代碼
裝飾器疑問🤔️
  • decoratorHoc是個函數,爲什麼支持@decoratorHoc?不要把這個看成理所固然。能夠試下把代碼拷入vscode,而後code runner,發現報了這段錯

目前瀏覽器不支持裝飾器模式,若是想要讓這段代碼跑起來的話,須要依賴babel的一個插件:babel-plugin-transform-decorators-legacy

那麼下面三步來把babel環境搞一下react

  • 安裝全局babel
<!--在當前index.js目錄下-->
yarn add babel-cli -g

yarn add babel-plugin-transform-decorators-legacy
複製代碼
  • 配置.babelrc
<!--在當前index.js目錄下新建.babelrc文件,內容以下-->
{
    plugins: ['transform-decorators-legacy']
}
複製代碼
  • 編譯index.js
<!--在當前index.js目錄下-->
babel index.js --out-file compiled.js
複製代碼

這裏把index.js編譯爲了compiled.js。而後code runner compiled.js,發現日誌輸出了"xuqiang"jquery

至此你就實現了裝飾器decoratorHoc

裝飾器模式應用場景

裝飾器模式能夠說是很是方便地擴展了一個對象。那麼平時開發中是否是有幾類代碼跟裝飾器模式很類似es6

  • react的高階組件
  • dva的@connect

這裏就舉兩個例子,更多場景須要你本身去發現了json

迭代器模式

迭代一個對象,就能夠稱爲是迭代器,下面從es6的迭代器來說解下後端

for-of遍歷json

這是一個es6的自定義迭代器DEMO,講解如何寫一個迭代器來遍歷普通對象,由於遍歷數組內置已經支持了設計模式

let obj = {
    name: 'xuqiang',
    age: 20,
    [Symbol.iterator]: () => {
        let props = Object.keys(obj), i = props.length;
        return {
            next(){
                if(i > 0){
                    i = i - 1;
                    return {
                        value: obj[props[props.length - i - 1]],
                        done: false
                    };
                }else{
                    return {
                        value: '',
                        done: true
                    };
                }
            }
        };
    }
}

for(let prop of obj){
    console.log(prop);
}
複製代碼

code runner發現能夠正確遍歷一個Object了api

發佈訂閱模式

發佈訂閱的場景

用一個老生常談的場景先來解釋下發布訂閱,那就是學生時代的訂牛奶,先來整理下需求數組

  • 有一個工廠,有一個let products = {},有一種屬性叫作牛奶。此屬性對應一個數組,數組存儲每一個訂奶牛人的數據。
  • 工廠有一個定時器,天天早上8點開始遍歷products,進行牛奶派發
  • 有不少消費者,向工廠訂牛奶

需求整理清楚了,下面用個DEMO來實現下

// 發佈訂閱模式
// 發佈訂閱模式

class Puber{
    constructor(name){
        this.name = name;
        this.products = {};
        this.startInterval();
    }

    startInterval(){
        setInterval(() => {
            for(let prop in this.products){
                if(this.products.hasOwnProperty(prop)){
                    this.emit(prop);
                }
            }
        }, 3000);
    }

    sub(evt, cb){
        if(!this.products[evt]){
            this.products[evt] = [];
        }
        this.products[evt].push(cb);
    }

    emit(evt){
        if(this.products[evt]){
            let cbs = this.products[evt];
            for(let cb of cbs){
                cb && cb();
            }
        }
    }
}

class Suber{
    constructor(name){
        this.name = name;
        this.puber = new Puber('徐強');
    }

    getPuber(){
        console.log(`我是${this.name},個人牛奶配送員是${this.puber.name}`);
    }

    sub(evt, cb){
        this.puber.sub(evt, cb);
    }
}

let customer1 = new Suber('劉思琪');
customer1.getPuber();
customer1.sub('牛奶', () => {
    console.log(`如今時間是${+new Date},牛奶送至將來科技城,收件人:劉思琪`);
});

let customer2 = new Suber('徐大佬');
customer2.getPuber();
customer2.sub('牛奶', () => {
    console.log(`如今時間是${+new Date},牛奶送至空港新城,收件人:徐大佬`);
});
複製代碼

發現劉思琪和徐大佬,已經成功訂了牛奶,而且天天早上10點鐘開始派發牛奶了。固然了,這裏沒有寫取消訂牛奶的操做,你們能夠本身接着寫

工廠模式

class jQuery{
    constructor(name){
        this.name = name;
    }
}

function $(name){
    return new jQuery(name);
}

let obj1 = $('xuqiang');
let obj2 = $('liusiqi');
console.log(obj1);
console.log(obj2);
複製代碼
工廠模式總結

這裏舉例也是用的jquery,能夠發現jquery的$函數就是用了工廠模式,那麼工廠模式有哪些好處呢?

  • 不須要本身調用new jQuery。直接用$很方便
  • 相似React.createElement,屏蔽了開發者直接使用new VNode,符合開放封閉原則,VNode的實現對開發者不可見

單例模式

光是單例模式的話比較簡單,感受沒什麼亮點,因此這裏結合裝飾器模式來寫一個單例模式

寫一個裝飾器模式+單例模式的騷操做
function getInstanceHoc(target){
    target.getInstance = (() => {
        let instance;
        return () => {
            if(!instance){
                instance = new Function(`return new ${target}()`)();
            }
            return instance;
        }
    })();
}

@getInstanceHoc
class Person{
    eat(){
        console.log('i am eating');
    }
}

let obj = Person.getInstance();
console.log(obj);
obj.eat();

let obj1 = Person.getInstance();
console.log(obj === obj1);
複製代碼

code runner以後發現,obj和obj1均可以eat了,而後比較obj===obj1,發現是true,說明單例模式已經成功了

代理模式

用es6的Proxy來說一下代理

class Vue{
    constructor(data){
        let _data =  data;

        return new Proxy(this, {
            get(target, key){
                return _data[key];
            },
            set(target, key, val){
                _data[key] = val;
                return true;
            }
        });
    }
}

let obj = new Vue({
    name: '徐強',
    age: 20
});
console.log(obj.name);
obj.name = '劉思琪';
console.log(obj.name);
複製代碼

外觀模式

外觀模式在jquery中很常見。在咱們平時開發中感受也很好用

function winAlert(title, message, buttons, cb){
    if(cb === undefined){
        cb = buttons;
        buttons = null;
    }

    console.log(title);
    console.log(message);
    console.log(buttons);
    console.log(cb);
}

winAlert('提示', '操做完成', ['確認', '取消'], () => {});

winAlert('提示', '操做完成', () => {});
複製代碼

運行結果:

這讓我想起目前業務中後端全部接口都是app/htmlGateway.do,只是version,rd等參數區別,從某種角度來講這是符合外觀模式的。封閉後端api的內部實現,開放htmlGetway給前端調用

可是目前這種方式有個問題是,對於yapi,rap2等mock平臺不太友好。由於api路徑都是一致的,會不太好mock,也多是由於我沒有發現解決方案吧,因此以爲不太好mock

ok 這幾種設計模式的demo都已經講完了。有問題能夠一塊兒談論。

相關文章
相關標籤/搜索