Vue雙向綁定的實現原理系列(二):設計模式

設計模式

一、裝飾者模式


github源碼
javascript

在不改變對象自身的基礎上,在程序運行期間給對象動態的添加職責vue

//看一個簡單的例子:
Function.prototype.fn = function(fn){
    var self = this;
    return function(){
        self.apply(this,arguments);
        fn.apply(this,arguments);
    }
}

function a(){
    console.log('我是函數a');
}

var copyA = a.fn(function(){
    console.log('我是a函數額外的功能');
})

copyA();  
// 我是函數a
// 我是a函數額外的功能

//監聽數組的變化
  var methods=['push','pop','shift','unshift','splice','slice','sort','reverse'];
    var Method = {};
    for(var i=0;i<methods.length;i++){
        var method = methods[i];
        (function(method){
            var original = Array.prototype[method];
            Method[method] = function(){
                console.log('監聽數組的變化或者操做函數等');
                return original.apply(this,arguments);
            }
        })(method)
    }
    var list = ['a','b','c'];
    list.__proto__ = Method;
    list.push('d');//打印:監聽數組的變化或者操做函數等
看一個段來自javascript面向對象編程指南(第二版)中關於裝飾器模式的解釋及其代碼:
裝飾器模式是一種結構型模式,它與對象的建立無關,主要考慮的是如何拓展對象的功能。也就是說,除了使用線性式(父-子-孫)繼承方式以外,咱們也能夠爲一個基礎對象建立若干個裝飾對象以拓展其功能。而後,由咱們的程序自行選擇不一樣的裝飾器,並按不一樣的順序使用它們。在不一樣的程序中咱們可能會面臨不一樣的需求,並從一樣的裝飾器集合中選擇不一樣的子集。
//裝飾一顆聖誕樹
    var tree = {};
    tree.decorate = function(){
        console.log('tree');
    }

    /*接着,再定義 getDecorator()方法,該方法用於添加額外的裝飾器。裝飾器被實現爲構造器函數,都繼承自 tree 對象。*/

    tree.getDecorator = function(deco){
        tree[deco].prototype = this;
        return new tree[deco];
    };
   
    /*下面來建立3個裝飾器,咱們將它設爲 tree 的一個屬性(以保持全局命名空間的純淨)。 如下對象也提供了 decorate()方法,注意它先調用了父類的decorate()方法。*/

    tree.Red = function(){
        this.decorate = function(){
            this.Red.prototype.decorate();
            // console.log(this.Red.prototype);
            // console.log(this.Red.prototype.decorate);
            console.log('red');
        };
        this.name = 'red';
    }
    tree.Blue = function(){
        this.decorate = function(){
            this.Blue.prototype.decorate();
            // console.log(this.Blue.prototype.decorate);
            //tree['Blue']的原型是tree,因此打印出"tree"
            console.log('blue');
        }
        this.name = 'blue';
    }
    tree.Angel = function(){
        this.decorate = function(){
            this.Angel.prototype.decorate();
            // console.log(this.Angel.prototype.decorate);
            console.log('angle');
        }
        this.name = 'angel';
    }

    /*把全部的裝飾器都添加到基礎對象中:*/

    tree = tree.getDecorator('Blue'); 
    tree = tree.getDecorator('Angel');
    tree = tree.getDecorator('Red');

    /*運行:*/
    tree.decorate();
    //tree
    //blue
    //angle
    //red

     /*解析:
        一、執行tree = tree.getDecorator('Blue'):
            tree['Blue'].prototype = tree;
            tree = {decorate: ƒ, name: "blue"}
            即tree['Blue']賦值給tree,tree['Blue']的原型指向tree
        輸出:
        "tree"
        "blue"

        二、執行tree = tree.getDecorator('Angel'):
            tree['Angel'].prototype = tree['Blue'],(這時候tree已經賦值爲tree['Blue'])
            tree = {decorate: ƒ, name: "Angle"}
            即tree['Angel']賦值給tree,tree['Angel']的原型指向tree['Blue']
        輸出:
        "angel"

        三、執行tree = tree.getDecorator('Red'):
            tree['Red'].prototype = tree['Angel'],(這時候tree已經賦值爲tree['Angel'])
            tree = {decorate: ƒ, name: "Red"}
            即tree['Red']賦值給tree,tree['Red']的原型指向tree['Angel']
        輸出:
        "red"
    */

/*
圖解:從下往上依次繼承
  tree = {decorate:fn,getDecorator:fn}
                    |
     tree['Blue'].prototype//tree={decorate: ƒ, name: "blue"}
                                    |
                        tree['Angel'].prototype//tree={decorate: ƒ, name: "Angle"} 
                                                        |
                                                 tree['Red'].prototype//tree={decorate: ƒ, name: "Red"}     
*/

二、觀察者模式(有時也稱爲發佈-訂閱模式)

看一個段來自javascript面向對象編程指南(第二版)中關於裝飾器模式的解釋及其代碼:

觀察者模式是一種行爲型模式,主要用於處理不一樣對象
之間的交互通訊問題。觀察者模式中一般會包含兩類對象。java

一個或多個發佈者對象:當有重要的事情發生時,會通知訂閱者。

一個或多個訂閱者對象:它們追隨一個或多個發佈者,監聽它們的通知,並做出
相應的反應git

var observer = {
        addSubscriber:function (callback){//添加訂閱者
            if(typeof callback === "function"){
                this.subscribers[this.subscribers.length] = callback;
            }
        },
        removeSubscriber:function (callback){//刪除訂閱者
            for(var i=0;i<this.subscribers.length;i++){
                if(this.subscribers[i] === callback){
                    delete this.subscribers[i];
                }
            }
        },
        publish:function (what) {//授受並傳遞數據給訂閱者
            for(var i=0;i<this.subscribers.length;i++){
                if(typeof this.subscribers[i] === "function"){
                    this.subscribers[i](what);
                }
            }
        },
        make:function(o){//將任意對象轉變爲一個發佈者併爲其添加上述方法。
            for(var i in this){//this->observer{addSubscriber: ƒ, removeSubscriber: ƒ, publish: ƒ, make:f}
                if(this.hasOwnProperty(i)){//observer.hasOwnProperty('addSubscriber') -> true
                    o[i] = this[i];
                    o.subscribers = [];
                }
            }//o-> {addSubscriber: ƒ, removeSubscriber: ƒ, publish: ƒ, make:f,subscribers:[],o.XX}
        }
    };

    //有個函數blogger和任意一個函數jack
    var blogger = {
        writeBlogPost : function(){
            var content = 'blogger';
            this.publish(content);
        }
    };
    var jack = {
        read:function (what){
            console.log('jack訂閱: '+what);
        }
    };

    //blogger變爲發佈者
    observer.make(blogger);

    //jack訂閱blogger
    blogger.addSubscriber(jack.read);

    //blogger發佈信息
    blogger.writeBlogPost();

    //輸出:jack訂閱: blogger

    最後: 別的函數也能夠成爲發佈者,
           blogger也能夠添加任意的函數爲訂閱者
           jack也能夠訂閱別的發佈者
以上總結爲:
        1.指定一個發佈者
        2.給發佈者添加緩存列表,存放回調函數,通知訂閱者
        3.發佈信息時,發佈者遍歷緩存表,觸發存放的回調函數
    下面看個簡單的例子:
var Event = function(){
        this.subs = {};
    }

    //添加收聽者:
    Event.prototype.addSubscriber=function(k,callback){
        if(!this.subs[k]){
            this.subs[k]=[];
        }
        this.subs[k].push(callback);
    };
    //發佈事件:
    Event.prototype.publish=function(k,item){
        var fns=this.subs[k];

        if(fns){//防止發佈給不存在的對象
            for(var i=0;i<fns.length;i++){
                fns[i](item)
            }
        }

    }

    function reader(item){
        console.log(item);
        console.log('我是收聽的');
        //console.log(arguments)
    }

    var event = new Event();
    event.addSubscriber('a',reader)
    event.addSubscriber('b',reader)
    event.publish('a','publish發佈信息');
    event.publish('b','publish發佈信息');//不存在的訂閱事件b

系列文章的目錄:

Vue雙向綁定的實現原理系列(一):Object.defineproperty
Vue雙向綁定的實現原理系列(二):設計模式
Vue雙向綁定的實現原理系列(三):監聽器Observer和訂閱者Watcher
Vue雙向綁定的實現原理系列(四):補充指令解析器compilegithub

相關文章
相關標籤/搜索