wengjq/Blog

js技巧篇--观察者模式

Opened this issue · 0 comments

1、前言

观察者模式( 又叫发布-订阅者模式 )应该是最常用的模式之一,在很多语言里都得到大量应用。包括我们平时接触的 dom 事件,也是 js 和 dom 之间实现的一种观察者模式。

document.body.addEventListener("click", function() {
    alert("Hello World");
});
document.body.click();

在这里需要监控用户点击 document.body 的动作,但是我们没办法预知用户将在什么时候点击。所以我们订阅了 document.body 的 click 事件,当 body 节点被点击时,body 节点便会向订阅者发布这个消息。那么到底什么是观察者模式呢?

先看看生活中的观察者模式。例如求职者去公司面试的时候,面试后每个面试官都会对求职者说:“有消息我们会通知你”。 在这里“我”是订阅者, 面试官是发布者。所以我不用每天或者每小时都去询问面试结果, 通讯的主动权掌握在了面试官手上。而我只需要提供一个联系方式。

2、定义

观察者模式定义了对象之间的一对多依赖关系,用于当一个对象改变状态时,所有的依赖关系都会被自动通知和更新。

观察者模式提供了一个订阅模型,其中对象订阅事件并在事件发生时得到通知。 这种模式是事件驱动的编程的基石,包括 JavaScript 。 观察者模式有利于良好的面向对象的设计,并促进松耦合。

示例图如下:

javascript-observer

3、什么时候使用

  • 当一个对象的状态或动作取决于另一个对象的状态或动作时。

  • 当更改一个对象时,需要更改未知数量的其他对象。

  • 当一个对象应该能够通知其他对象的变化而不知道这些其他对象。

4、示例

var pubsub = {};

(function(q) {

    var topics = {},
        subUid = -1;

    // 使用特定主题名称和参数(如要传递的数据)发布事件
    q.publish = function(topic, args) {
        if (!topics[topic]) {
            return false;
        }

        var subscribers = topics[topic],
            len = subscribers ? subscribers.length : 0;

        while (len--) {
            subscribers[len].func( topic, args );
        }

        return this;
    };

    // 当观察到话题/事件时,用特定的话题名称和回调函数来订阅事件
    q.subscribe = function(topic, func) {
        if (!topics[topic]) {
            topics[topic] = [];
        }

        var token = ( ++subUid ).toString();

        topics[topic].push({
            token: token,
            func: func
        });

        return token;
    };

    // 根据对订阅的标记化引用,取消订阅特定主题
    q.unsubscribe = function(token) {
        for (var m in topics) {
            if (topics[m]) {
                for (var i = 0, j = topics[m].length; i < j; i++) {
                    if (topics[m][i].token === token) {
                        topics[m].splice( i, 1 );
                        return token;
                    }
                }
            }
        }

        return this;
    };
}(pubsub));

var messageLogger = function (topics, data) {
    console.log( "Logging: " + topics + ": " + data );
};
// 订阅
var subscription = pubsub.subscribe("inbox/newMessage", messageLogger);

// 发布
pubsub.publish("inbox/newMessage", "hello world!");

// 退订
//pubsub.unsubscribe(subscription);

// 发布
pubsub.publish("inbox/newMessage", "Hello! are you still there?");