9

设计模式

1、装饰者模式


github源码

在不改变对象自身的基础上,在程序运行期间给对象动态的添加职责

//看一个简单的例子:
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

     /*解析:
        1、执行tree = tree.getDecorator('Blue'):
            tree['Blue'].prototype = tree;
            tree = {decorate: ƒ, name: "blue"}
            即tree['Blue']赋值给tree,tree['Blue']的原型指向tree
        输出:
        "tree"
        "blue"

        2、执行tree = tree.getDecorator('Angel'):
            tree['Angel'].prototype = tree['Blue'],(这时候tree已经赋值为tree['Blue'])
            tree = {decorate: ƒ, name: "Angle"}
            即tree['Angel']赋值给tree,tree['Angel']的原型指向tree['Blue']
        输出:
        "angel"

        3、执行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"}     
*/                                                                                                  

2、观察者模式(有时也称为发布-订阅模式)

看一个段来自javascript面向对象编程指南(第二版)中关于装饰器模式的解释及其代码:

观察者模式是一种行为型模式,主要用于处理不同对象
之间的交互通信问题。观察者模式中通常会包含两类对象。

一个或多个发布者对象:当有重要的事情发生时,会通知订阅者。

一个或多个订阅者对象:它们追随一个或多个发布者,监听它们的通知,并作出
相应的反应

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双向绑定的实现原理系列(四):补充指令解析器compile


平平不平
343 声望18 粉丝