简单理解观察者模式(pub/sub)在前端中的应用

概念

观察者模式被普遍地应用于JavaScript客户端编程中。全部的浏览器事件(mouseover,keypress等)都是使用观察者模式的例子。这种模式的另外一个名字叫“自定义事件”,意思是这些事件是被编写出来的,和浏览器触发的事件相对。它还有另一个名字叫“订阅者/发布者”模式。
使用这个模式的最主要目的就是促进代码触解耦。在观察者模式中,一个对象订阅另外一个对象的指定活动并获得通知,而不是调用另外一个对象的方法。订阅者也被叫做观察者,被观察的对象叫做发布者或者被观察者(译注:subject,不知道如何翻译,第一次的时候译为“主体”,第二次译时以为不妥,仍是直接叫被观察者好了)。当一个特定的事件发生的时候,发布者会通知(调用)全部的订阅者,同时还可能以事件对象的形式传递一些消息。 (摘自javascript 模式一书)javascript

维基百科定义:在软件架构中,发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不一样的类别,无需了解哪些订阅者(若是有的话)可能存在。一样的,订阅者能够表达对一个或多个类别的兴趣,只接收感兴趣的消息,无需了解哪些发布者(若是有的话)存在。html

理解观察者模式:

摘自 http://www.cnblogs.com/tugenh...java

JS传统事件就是一个观察者模式,之因此要有观察者模式,是由于有时候和传统事件无关的事件,好比:2个或者更多模块的直接通讯问题,好比说我有个index.html页面,我有不少JS文件,好比:git

a.js: function a(){}; b.js: function b(){}; c.js function c(){}; 等等。后面还有许多这样的JS,那么我要在index.html初始化这些函数的话,我须要这样调用a();b();c()等等,也就是说页面调用的时候 我要这样调用,增长了依赖性,我要知道有多少个函数要这样初始化调用,可是若是咱们如今用观察者模式就不须要知道有哪些订阅者,好比一个模块(或者多个模块)订阅了一个主题(或者事件),另外一个模块发布这个主题时候,订阅这个主题模块就能够执行了,观察者主要让订阅者与发布者解耦,发布者不须要知道哪些模块订阅了这个主题,它只管发布这个主题就能够了,一样订阅者也无需知道那个模块会发布这个主题,它只管订阅这个主题就能够了。这样2个模块(或更多模块)就实现了关联了。而不须要和上面代码同样,我要知道哪些模块要初始化,我要怎样初始化。这只是一个简单的列子解释观察者模式要使用在什么地方,我也看过不少博客关于这方面的资料,可是不少人写博客只是讲了如何实现观察者模式及观察者模式的好处,并无讲咱们何时该使用观察者模式,因此我列举了上面的列子,就是多个不一样业务模块须要相互关联的时候,能够使用观察者模式。就比如requireJS,seaJS,KISSY解决依赖的问题同样(好比A依赖于B,B依赖于C,只要一个解决入口文件,其余都会异步加载出来同样)。也就是说各个模块之间的关联性能够使用观察者模式来设计。github

代码实现:在该篇博客(https://www.cnblogs.com/Lucky...编程

(function(){
    var Event = {
        on: function (type, handler) {
            if (!this.handlers) {
                // this.handlers = {};    
                Object.defineProperty(this, "handlers", {
                    value: {},
                    enumerable: false,  // important 避免拷贝Event时污染handlers
                    configurable: true,
                    writable: true
                })
            }   
            if (typeof this.handlers[type] === 'undefined') {
                this.handlers[type] = [];
            }
            this.handlers[type].push(handler);
        },
        emit: function (eventName) {
            if (this.handlers[arguments[0]] instanceof Array) {
                var handlers = this.handlers[arguments[0]];
                for (var i=0; i<handlers.length; i++) {
                    handlers[i](arguments[1].message);
                }
            }
        },
        unsubscribe: function (type, handler) {
            if (this.handlers[type] instanceof Array) {
                var handlers = this.handlers[type];
                for (var i=0; i<handlers.length; i++) {
                    if (handlers[i] === handler) {
                        break;
                    }
                }
                handlers.splice(i, 1);
            }
        }
    };
  
    Event.on('test', function (message) {
        console.log(message);
    });
    Event.on('test', function () {
        console.log('test');
    });

    Event.emit('test', {message: 'hello world'});

    var person1 = {
        sayHello: function (message) {
            console.log(message);
        }
    };
    var person2 = {
        sayHello: function (message) {
            console.log(message);
        }
    };
    Object.assign(person1, Event);
    Object.assign(person2, Event);
    person1.on('call1', person1.sayHello);
    person2.on('call2', person2.sayHello);
    person1.emit('call1', {message:'person1 is calling call1.'}); // 'person1 is calling call1.' 
    person1.emit('call2', {message:'p111'}); //  no output
    person2.emit('call1', {message:'p222'}); //  no output
    person2.emit('call2', {message:'person2 is calling call2.'}); // 'person2 is calling call2'
    person1.unsubscribe('call1', person1.sayHello);
    person1.emit('call1', {message:'person1 is calling call1 again.'}); // no output
    person1.on('call1', person1.sayHello);
    person1.emit('call1', {message:'person1 is calling call1 again.'}); // person1 is calling call1 again.

    let paper = Object.assign({}, Event);
    let mike = {
        haveABreak: function (news) {
            let msg = 'Mike just read news: ' + news;
            console.log('-------------');
            console.log(msg);
        }
    };
    let tom = {
        haveABreak: function (news) {
            let msg = 'Tom just read news: ' + news;
            console.log(msg);
            console.log('-------------');
        }
    };
    paper.on('dailyNews', mike.haveABreak);
    paper.on('dailyNews', tom.haveABreak);
    paper.emit('dailyNews', {message: 'Coding is getting better.'});

})()

可在个人github上下载上述代码(https://github.com/ylzsmallsu...),并包含JavaScript模式中观察者模式例子。浏览器

此外,观察者模式还可用于实现数据绑定

思想:使用数据特性来为 HTML 代码进行绑定,全部被绑定在一块儿的 JavaScript 对象和 DOM 元素都会订阅一个 PubSub 对象。只要 JavaScript 对象或者一个 HTML 输入元素监听到数据的变化时,就会触发绑定到 PubSub 对象上的事件,从而其余绑定的对象和元素都会作出相应的变化。架构

参考文章

本篇文章主要是对查阅的几篇文档集中梳理,有兴趣的能够翻阅以上参考文章。异步

相关文章
相关标签/搜索