观察者模式

观察者模式(Observer) 通常又被称为发布-订阅者模式或消息机制, 它定义了对象间的一种一对多的依赖关系, 只要当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新, 解决了主体对象与观察者之间功能的耦合, 即一个对象状态改变给其他对象通知的问题。

1、什么是观察者模式

观察者模式(Observer) 通常又被称为发布-订阅者模式消息机制, 它定义了对象间的一种一对多的依赖关系, 只要当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新, 解决了主体对象与观察者之间功能的耦合, 即一个对象状态改变给其他对象通知的问题。

下面这段代码就是一种发布-订阅模式:

document.querySelector('#btn').addEventListener('click',function () {
    alert('You click this btn');
}, false)

除了我们常见的 DOM 事件绑定外,观察者模式应用的范围还有很多~

比如比较当下热门 vue 框架,里面不少地方都涉及到了观察者模式,比如:

数据的双向绑定

利用Object.defineProperty()对数据进行劫持, 设置一个监听器Observer,用来监听所有属性, 如果属性上发上变化了,就需要告诉订阅者Watcher去更新数据, 最后指令解析器Compile解析对应的指令, 进而会执行对应的更新函数,从而更新视图,实现了双向绑定~

子组件与父组件通信

Vue中我们通过props完成父组件向子组件传递数据, 子组件与父组件通信我们通过自定义事件即$on、$emit来实现, 其实也就是通过$emit来发布消息,并对订阅者$on做统一处理~

2、创建一个观察者

首先我们需要创建一个观察者对象,它包含一个消息容器和三个方法,分别是订阅消息方法on,取消订阅消息方法off,发送订阅消息publish。

const Observe = (function() {
    // 防止消息队列暴露而被篡改,将消息容器设置为私有变量
    let __message = {};
    return {
        // 注册消息接口
        on: function(type, fn) {
            // 如果此消息不存在,创建一个该消息类型
            if (typeof __message[type] === 'undefined') {
                //  将执行方法推入该消息对应的执行队列中
                __message[type] = [fn];
            } else {
                // 如果此消息存在,直接将执行方法推入该消息对应的执行队列中
                __message[type].push(fn);
            }
        },

        // 发布消息接口
        publish: function(type, args) {
            // 如果该消息没有注册,直接返回
            if (!__message[type]) return;
            // 定义消息信息
            const events = {
                type, // 消息类型
                args: args || {} // 参数
            }
            // 遍历执行函数
            for (let i = 0, len = __message[type].length; i < len; i++) {
                // 依次执行注册消息对应的方法
                __message[type][i].call(this, events)
            }
        },

        // 移除消息接口
        off: function(type, fn) {
            // 如果消息执行队列存在
            if (__message[type] instanceof Array) {
                // 从最后一条依次遍历(之所以从最后一个开始遍历,是因为下面会用到splice)
                for (let i = __message[type].length - 1; i >= 0; i--) {
                    // 如果存在该执行函数则移除相应的动作
                    __message[type][i] === fn && __message[type].splice(i, 1);
                }
            }
        }
    }
})();

使用:

//订阅消息
Observe.on('say', function (data) {
    console.log(data.args.text);
})
Observe.on('success',function () {
    console.log('success')
});

//发布消息
Observe.publish('say', { text : 'hello world' } )
Observe.publish('success');

 

我们在消息类型为say、success的消息中注册了两个方法,其中有一个接受参数,另一个不需要参数, 然后通过publish发布say和success消息,结果跟我们预期的一样, 控制台输出了 hello world 以及 success ~

参考资料

Was this helpful?

0 / 0

发表评论 0

Your email address will not be published. Required fields are marked *