javascript设想形式进修——观察者形式

Javascript活泼在事宜驱动的环境中,比方鼠标的相应、事宜的回调、收集的要求等,视察者形式又称宣布者-定阅者(publisher-subscriber)形式,是处置惩罚对象及其行动和状况之间的关联,管理人与使命之间的关联。

1. 最常见的视察者形式

1.1 事宜监听器
document.body.addEventListener('click', function () {
    console.log('you clicked me, poor guy!')
});

这是最简朴最平常的一种视察者形式,除此click 之外另有loadblurdragfocusmouseover、等。事宜监听器(listener)有别于事宜处置惩罚器(handler),在事宜监听器中,一个事宜能够关联多个监听器,每一个监听器自力处置惩罚监听到的音讯;事宜处置惩罚器是实行处置惩罚事宜发生后的关联函数,一种事宜是能有一个处置惩罚函数:

var dom = $('.dom');
var listener1 = function(e){
    //do one thing
}
var listener2 = function(e){
    //do another thing
}
addEvent(dom,'click',listener1);
addEvent(dom,'click',listener2);

在这个事宜监听器的例子中,listener1listener2 都是dom元素的监听器,当dom被点击时,都邑实行各自的函数;

var dom = document.getElementById('dom');
var handler1 = function(e){
    //do one thing
}
var handler2 = function(e){
    //do another thing
}
dom.onclick = handler1;
dom.onclick = handler2;

在这个事宜处置惩罚器的例子中,handler1不会被实行,只实行handler2,是一次赋值的操纵。

1.2 动画

在动画中普遍运用了视察者形式,动画的最先、完成、停息等,都须要视察者来肯定物体的行动和状况。

//定义动画
var Animation = function(){
    this.onStart = new Publisher;  //关于Publisher的设想将在1.3节引见
    this.onComplete = new Publisher;
    this.onTween = new Publisher;
}
//定义一个原型要领
Animation.prototype.look = function(){
    this.onStart.deliver('animation started!');
    this.onTween.deliver('animation is going on!');
    this.onComplete.deliver('animation completed!');  
};

//实例一个box对象
var box = new Animation();

//定义三个函数作为subscribers
var openBox = function(msg){
    console.log(msg)
}
var checkBox = function(msg){
    console.log(msg)
}
var closeBox = function(msg){
    console.log(msg)
}

//定阅事宜
openBox.subscribe(box.onStart);
checkBox.subscribe(box.onTween);
closeBox.subscribe(box.onComplete);

//挪用要领
box.look()

//animation started!
//animation is going on!
//animation completed!

1.3 视察者的构建

起首,须要一个宣布者。先定义一个组织函数,为其定义一个数组,用以保留定阅者信息:

function Publisher(){
    this.subscribes = [];
}

宣布者具有宣布音讯的功用,定义一个deliver的原型函数:

Publisher.prototype.deliver = function(data){
    this.subscribes.forEach(function(fn){
        fn(data);
    });
    return this;
}

接下来组织定阅要领:

Function.prototype.subscribe = function(publisher){
    var that = this;
    var alreadyExists = publisher.subscribes.some(function(el){
        return el === that;
    });
    if(!alreadyExists){
        publisher.subscribes.push(this);
    }
    return this;
}

直接在Function的prototype增加subscribe要领,如许一切函数都能够挪用该要领。如许就构建终了了,运用要领参看1.2动画的用例。
比较直观的诠释(以onStart为例):box对象实行look要领时,实行onStart.deliver(),将onStart事宜宣布出去,播送关照'animation started!',这个时刻,一直在监听onStartopenBox监听到该事宜宣布的信息,打印出来。

1.4 另一种构建视察者的体式格局

这类体式格局模拟了nodejs的事宜处置惩罚机制,代码也比较简约:

    var scope = (function() {
    //音讯列表
    var events = {};
    return {
        //定阅音讯
        on:function(name,hander){
            var index = 0;  //纪录音讯时候的索引
            if(events[name]){  
                //音讯名已存在,将处置惩罚函数放到该音讯的事宜行列中
                index = events[name].push(hander) - 1; 
            }else{
                events[name] = [hander];
            }
            //返回当前音讯处置惩罚事宜的移除函数
            return function(){
                events[name].splice(index,1);
            }
        },
        //封闭音讯
        off:function(name){
            if(!events[name]) return;
            //音讯存在,删除音讯
            delete events[name];
        },
        //音讯宣布
        emit:function(name,msg){
            //音讯不存在,不处置惩罚
            if(!events[name]) return;
            //音讯存在,将该事宜处置惩罚行列中每一个函数都实行一次
            events[name].forEach(function(v,i){
                v(msg);
            });
        }
    }
})();

var sayHello = scope.on('greeting',function(msg){
    console.log('定阅音讯:' + msg);
});

var greeting = function(msg){
    console.log('宣布音讯:' + msg);
    scope.emit('greeting', msg);
}

greeting('hello Panfen!') 

1.5 nodejs中视察者形式的完成计划

nodejs中有events模块来完成视察者形式,可参考Nodejs API-Events 谈视察者形式,大多数的模块都集成了events模块,所以能够直接运用emit发射事宜和on监听事宜,或许像下面如许先定义一下;

var EventEmitter = require('events').EventEmitter;
var life = new EventEmitter();
life.setMaxListeners(11);       //设置最大监听数,默许10

//宣布和定阅sendName
life.on('sendName',function(name){
    console.log('say hello to '+name);
});
life.emit('sendName','jeff');

//宣布和定阅sendName2
function sayBeautiful(name){
    console.log(name + ' is beautiful');
}
life.on('sendName2',sayBeautiful);
life.emit('sendName2','jeff');

经常使用要领:

  • hasConfortListener :用于推断发射的事宜是不是有监听器

  • removeListener :移除监听器

  • listenerCount :该事宜一切监听器的总数

  • removeAllListeners :移除事宜一切(或某个)的监听器

1.6 总结

视察者形式建立了推送收听的逻辑,适用于愿望把人的行动和应用程序的行动离开的场所。举个例子来讲:用户点击导航栏的一个tab时,会打开包括更多选项的子菜单,平常会挑选在晓得哪一个元素的情况下直接监听这个click事宜,如许做的弊病在于完成了与click事宜直接绑在一同。更好的做法是:建立一个可视察的onTabChange对象,关联多少视察者完成。

1.7 参考资料:

    原文作者:芒果屋里的猫
    原文地址: https://segmentfault.com/a/1190000008746120
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞