1. 程式人生 > 其它 >JavaScript的常見設計模式

JavaScript的常見設計模式

技術標籤:JavaScriptjavascriptes6

一、設計模式:

是一套經過反覆使用、多人知曉的、經過分類的、程式碼設計經驗的總結。
優點:可以讓程式碼更容易被他人理解、保證程式碼的可靠性和可重用性。
W3shchol的設計模式分類:構造器模式、模組化模式、暴露模組模式、單例模式、中介者模式、原型模式、命令模式、外觀模式、工廠模式、Mixin模式、裝飾模式、亨元(Flyweight)模式、MVC模式、MVP模式、MVVM模式、組合模式、介面卡模式、外觀模式、觀察者模式、迭代器模式、惰性初始模式、代理模式、建造者模式。共23種

二、常見的設計模式例項

  1. 工廠模式:

    • 建構函式+new關鍵字建立物件
    • 建立物件的模式
    • 批量建立同類物件
    • 原料,加工,出廠
function CreateObj(n, a){
    this.name = n;
    this.age = a;
    this.show = function(){
        console.log(this.name + "===" + this.age);
    }
}
var p1 = new CreateObj("張三",18);
var p2 = new CreateObj("李四",19);
p1.show();
p2.show();
  1. 單例模式:
    • 單個例項
    • 當某些功能,需要被重複使用時,無關乎到底有幾個物件,只要有至少一個就可以投入使用
    • 寫法不定,只要在重複使用或執行過程中只同時存在一個物件,即可
//多次建立,返回同一個物件
function fn(){
    if(!fn.obj) { //給函式新增一個屬性。 因為函式是全域性的,所以該屬性一旦新增一直存在;
         fn.obj = {
             name : “liyang"
         };
    }
    return fn.obj;
}
var obj1 = new fn();
var obj2 = new fn();
console.log
(obj1 == obj2);
  1. 觀察者模式—(釋出訂閱者模式)
    • 定義例一種一對多的關係,讓多個觀察者物件同時監聽某一個主題物件,對這個主題物件的狀態發生變化時就會通知所有的嗎觀察者物件,使得他們能夠自動更新自己。
    • 優點:
      • 支援簡單的廣播通訊,自動通知所有已經訂閱的物件
      • 頁面載入後目標物件很容易與觀察者存在一種動態關聯,增加了靈活性
      • 目標物件與觀察者之間的抽象耦合關係能夠單獨擴充套件以及重用
 class AddEvent{
        constructor(){
            // 記錄資訊的小本本
            // this.msg = {
            //     張三:[扣分,罰錢,俯臥撐],
            //     李四:[寫檢討,罰錢]
            // }
            this.msg = {};
        }
        add(type, cb){
            // 新增某個型別和對應的處理函式
            // 新增之前先判斷是否已經存在
            if(this.msg[type]){
                // 如果存在,就新增
                this.msg[type].push(cb);
            }else{
                // 如果不存在,就建立並賦值
                this.msg[type] = [cb];
            }
        }
        remove(type, cb){
            // 刪除某個型別的某個處理函式
            // 刪除之前先判斷是否存在,不存在就不用執行了
            if(!this.msg[type]) return;
            // 存在,找到本次要刪除的函式
            for(var i=0;i<this.msg[type].length;i++){
                if(this.msg[type][i] === cb){
                    break;
                }
            }
            // 從陣列中剔除
            this.msg[type].splice(i,1);
        }
        emit(type){
            // 觸發某個型別的所有的處理函式
            // 觸發之前先判斷是否存在,不存在就不用執行了
            if(!this.msg[type]) return;
            // 存在,拿到每個處理函式
            for(var i=0;i<this.msg[type].length;i++){
                // 執行
                this.msg[type][i]();
            }
        }
    }

    var ls = new AddEvent();
    ls.add("張三", fn1)
    function fn1(){
        console.log("扣分")
    }
    ls.add("張三", fn2)
    function fn2(){
        console.log("罰錢")
    }
    ls.add("張三", fn3)
    function fn3(){
        console.log("俯臥撐")
    }

    // ls.remove("張三",fn1);
    ls.remove("張三",fn2);
    // ls.remove("張三",fn3);


    ls.emit("張三");

    console.log(ls);


    ls.add("李四", function(){
        console.log("開除")
    })
  1. 策略模式:
    • 計劃
    • 提前定義或規劃好要執行的功能1,功能2,功能3…在程式執行過程中,根據動態產生的不同的狀態,決定真正要執行的功能
  2. 介面卡模式:
    • 介面卡,容錯
    • 介面卡的意義,多數應用在系統介面使用,也就是別人提供了一個功能,但要求傳入一個A型別的引數
    • 而我們手裡的資料是B型別的,如果我們想使用這個功能。那麼有兩種解決辦法:
    • 第一,把自己的原始碼進行修改,讓B型別改為A型別,這是非常蠢的做法。
    • 第二,我們把B型別的資料進行一個包裝,讓它看起來符合型別A,這個包裝函式,就是介面卡
//我們要對所有產品都進行一個標準化的測試,測試流程其中包括了
電話、簡訊、遊戲、音樂等等功能
 
//但是對於一個平板來說,電話功能是無法使用的,因此測試會出問題。
 所以將平板進行了包裝(類似於代理)
 
//這樣就簡單的解決了相容問題
function test(product) {
    try {
         product.phonecall();
    } catch(e) {
         console.log("電話功能測試失敗!")
    }
    try {
         product.playgame();
    } catch(e) {
         console.log("遊戲功能測試失敗!")
    }
}
 
function Phone(){
    this.phonecall = function(){}
    this.playgame = function(){}
   this.toString = function(){
         return "電話";
    }
}
function Pad(){
    this.playgame = function(){}
    this.toString = function(){
         return "平板";
    }
 
}
//介面卡的意義,多數應用在系統介面使用,也就是別人提供了一個功能,但要求傳入一個A型別的引數
//而我們手裡的資料是B型別的,如果我們想使用這個功能。那麼有兩種解決辦法:
//第一,把自己的原始碼進行修改,讓B型別改為A型別,這是非常蠢的做法。
//第二,我們把B型別的資料進行一個包裝,讓它看起來符合型別A,這個包裝函式,就是介面卡。
 
function Adapter(product){

    this.phonecall = function(){
         if(product.phonecall) {
             product.phonecall();
         } else {
             console.log("this "+ product.toString() + " is not support function phonecall!")
         }
    }
    this.playgame = function(){
         if(product.playgame) {
             product.playgame();
         }
    }
}
test(new Phone());
test(new Adapter(new Pad()));
  1. 組合模式
    • 組合物件或類,為了實現批量操作
    • 組合成樹狀結構,類似於HTML結構的結構
    • 組合模式就是為動態的HTML而生的
    • 組合器,用來將物件組合起來
function ImagesStore( id ){
        // 準備用來儲存子物件(枝物件或葉物件)
        this.children = [];
        this.element = document.createElement("div");
        this.element.id = id;
        document.body.appendChild(this.element)
    }
    ImagesStore.prototype = {
        constructor : ImagesStore,
        add:function( child ){
            this.children.push( child );
            this.element.appendChild( child.getElement() );
        },
        remove:function( child ){
            for( var i=0; i< this.children.length; i++ ){
                if( this.children[i] === child ){
                    this.children.splice( i, 1 );
                    break;
                }
            }
            this.element.removeChild( child.getElement() );
        },
        show:function(){
            this.element.style.border = 'solid 10px black';
            for( var i=0; i<this.children.length; i++ ){
                this.children[i].show();
            }
        },
        hide:function(){
            for( var i=0; i<this.children.length; i++ ){
                this.children[i].hide();
            }
            this.element.style.border = 'none';
        },
        getElement:function(){
            return this.element;
        }
    }


    function Son(img){
        this.abc = document.createElement("img");
        this.abc.src = img;
    }
    Son.prototype = {
        constructor: Son,
        add:function( child ){
            console.log("這是一個葉物件,無法新增子物件了")
        },
        remove:function( child ){
            console.log("這是一個葉物件,無法刪除子物件了")
        },
        show:function(){
            this.abc.style.border = "solid 10px #aaa";
        },
        hide:function(){
            this.abc.style.border = "none";
        },
        getElement:function(){
            return this.abc;
        }
    }

    var s1 = new Son("https://dss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2926199504,3558053336&fm=26&gp=0.jpg");
    var s2 = new Son("https://dss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3153405721,1524067674&fm=26&gp=0.jpg");
    var p = new ImagesStore("box1");
    p.add(s1);
    p.add(s2);

    p.show();
  1. 代理模式
    • 代理,委託,自己不想做,交給別人做
    • 為了攔截專案中某些已經固定執行模式的資料或資訊,進行二次使用或另作他用
    • 代理模式分成兩個部分,一個部分是本體; 即為你想要實現的功能;另一部分為代理;代理可以代替本體例項化;
      代理一般使用在非常耗時的資料上,也會用在體積十分龐大的本體上。
    • 一句話總結代理模式:為其他物件提供代理,以控制這個物件的訪問;
//舉個簡單的例子:

 //小夥子想要送花給小姑娘,但是不好意思送,於是找到了快遞小哥,小哥幫忙送花;在這裡小哥就是代理!

//小姑娘(姑娘的名字);

vargirl=function(name){
    this.name=name;
}

//小夥子(想要送給誰);

varboy=function(girl){
    this.girl=girl;
    this.sendGift=function(gift){
        alert("你好,漂亮的"+this.girl.name+",這是我送你的:"+gift);
    }
}
//快遞小哥(知道送給誰);

varporxyLitterBrother=function(girl){
    this.girl=girl;
    this.send=function(gift){
        gift="一個吻";
        (newboy(girl)).sendGift(gift);
    }
}

現在的送花就變得簡單了;

varporxy= newporxyLitterBrother(newgirl("花花"));
porxy.send("鑽戒");

一個人替你去做一些事情。 這個人保不齊還能撈點好處, 這就是代理模式;

  1. MVC模式
    • M:model:模型,管理資料
    • V:view:檢視,管理資料在頁面上的渲染方式
    • C:ctrl:控制器,中央控制中心
    • 工作流程:
    • 使用者向控制器傳送一個指令
    • 控制器根據指令到模型中取出對應的資料並儲存
    • 控制器根據指令到檢視中取出一個檢視並將資料發給這個檢視
    • 檢視決定資料以何種方式呈現給使用者
 class Model{
        getData1(){
            return "hello";
        }
        getData2(){
            return "設計模式";
        }
        getData3(){
            return "和";
        }
        getData4(){
            return "即將到來的";
        }
        getData5(){
            return "jQuery";
        }
    }

    class View{
        view1(d){
            console.log(d);
        }
        view2(d){
            alert(d);
        }
        view3(d){
            document.write(d);
        }
    }

    class Ctrl{
        constructor(){
            this.m = new Model();
            this.v = new View();
        }
        ctrl1(){
            this.d = this.m.getData3();
            this.v.view2(this.d);
        }
        ctrl2(){
            this.d = this.m.getData1();
            this.v.view3(this.d);
        }
        ctrl3(){
            this.d = this.m.getData2();
            this.v.view1(this.d);
        }
        ctrl4(){
            this.d = this.m.getData4();
            this.v.view2(this.d);
        }
    }


    var c = new Ctrl();

    c.ctrl3();