博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
js设计模式笔记 - 观察者模式
阅读量:7249 次
发布时间:2019-06-29

本文共 3923 字,大约阅读时间需要 13 分钟。

观察者模式,定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。

事实上,只要你曾经在DOM节点上绑定过事件函数,那么你就曾经使用过观察者模式了!

document.body.addEventListener('click', function () {    alert(2);});

但是这只是对观察者模式最简单的使用,在很多场景下我们经常会实现一些自定义事件来满足我们的需求。

举个例子:你去一家公司应聘,谈了一顿下来,hr跟你说:"好了,你回去等通知吧!"。这个时候,1.你会问公司的电话,然后每天打过去问一遍结果          2.把自己的手机号留给hr,然后等他给你打电话          相信很多时候呢,大家都是选择了后者。万一你每天给hr打电话弄烦他了,或许他本来打算招你的,现在也不再打算再鸟你啦!那么这个时候,hr就相当于一个发布者,而你就是一个订阅者啦!好吧,大部分叫你回去等消息的就等于没救啦......我还遇到过一个如果你没被录取,就连通知都不通知你的公司!

那么一个简单的观察者模式应该怎么实现呢?

  1. 要指定一个发布者;
  2. 给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;(这家公司很多人来应聘)
  3. 最后发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数;(你up or 你over)
var event = {};    //发布者(hr)event.clietList = []; //发布者的缓存列表(应聘者列表)event.listen = function(fn) { //增加订阅者函数    this.clietList.push(fn);};event.trigger = function() { //发布消息函数    for (var i = 0; i < this.clietList.length; i++) {        var fn = this.clietList[i];        fn.apply(this, arguments);    }};event.listen(function(time) { //某人订阅了这个消息    console.log('正式上班时间:' + time);});event.trigger('2016/10',yes); //发布消息//输出 正式上班时间:2016/10

到这里,我们已经实现了一个最简单的观察者模式了!

但是上面的函数其实存在一个问题,那就是发布者没办法选择自己要发布的消息类型!

比如这家公司同时在招php,web前端,如果使用上面的函数就没办法区分职位了!只能一次性把全部订阅者都发送一遍消息。
对上面的代码进行改写:

var event = {}; //发布者(hr)event.clietList = []; //发布者的缓存列表(应聘者列表)event.listen = function(key, fn) { //增加订阅者函数    if (!this.clietList[key]) {        this.clietList[key] = [];    }    this.clietList[key].push(fn);};event.trigger = function() { //发布消息函数    var key = Array.prototype.shift.call(arguments),        fns = this.clietList[key];    for (var i = 0; i < fns.length; i++) {        var fn = fns[i];        fn.apply(this, arguments);    }};event.listen('web前端', fn1 = function(time) { //小强订阅了这个消息。    console.log('姓名:小强');    console.log('正式上班时间:' + time);});event.listen('web前端', fn2 = function(time) { //大大强订阅了这个消息    console.log('姓名:大大强');    console.log('正式上班时间:' + time);});//发布者发布消息event.trigger('web前端','小强', '2016/10'); //姓名:小强   正式上班时间:2016/10  event.trigger('php','大大强', '2016/15'); //姓名:大大强   正式上班时间:2016/15

通过添加了一个key,我们实现了对职位的判断。

有了订阅事件,我们怎么能少了取消订阅事件呢?

event.remove = function(key, fn) {    var fns = this.clietList[key];    if (!fns) {        return false;    }    if (!fn) { //如果没有传入fn回调函数,直接取消key对应消息的所有订阅        this.clietList[key] = [];    } else {        for (var i = 0; i < fns.length; i++) { //遍历回调函数列表            var _fn = fns[i];            if (_fn === fn) {                fns.splice(i, 1); //删除订阅者的回调函数            }        }    }};//这时候必须指定回调函数,否则无法在remove函数中进行对比删除。event.listen('web前端', fn1 = function(time) { //小强订阅了这个消息。    console.log('姓名:小强');    console.log('正式上班时间:' + time);});event.listen('web前端', fn2 = function(time) { //大大强订阅了这个消息    console.log('姓名:大大强');    console.log('正式上班时间:' + time);});event.remove('web前端',fn1);//发布者发布消息event.trigger('web前端','2016/10');//输出 姓名:大大强   正式上班时间:2016/10

对上面代码进行改进,创建一个全局对象来实现观察者模式,

使用闭包实现私有变量,仅暴露必须的API给使用者:

var event = (function() {    var clietList = []; //发布者的缓存列表(应聘者列表)    var listen = function(key, fn) { //增加订阅者函数        if (!this.clietList[key]) {            this.clietList[key] = [];        }        this.clietList[key].push(fn);    };    var trigger = function() { //发布消息函数        var key = Array.prototype.shift.call(arguments),            fns = this.clietList[key];        for (var i = 0; i < fns.length; i++) {            var fn = fns[i];            fn.apply(this, arguments);        }    };    var remove = function(key, fn) {        var fns = this.clietList[key];        if (!fns) {            return false;        }        if (!fn) { //如果没有传入fn回调函数,直接取消key对应消息的所有订阅            this.clietList[key] = [];        } else {            for (var i = 0; i < fns.length; i++) { //遍历回调函数列表                var _fn = fns[i];                if (_fn === fn) {                    fns.splice(i, 1); //删除订阅者的回调函数                }            }        }    };        return{        listen:listen,        trigger:trigger,        remove:remove    }})();

观察者模式进阶:

  1. 使用命名空间防止事件名冲突
  2. 实现先发布后订阅功能

转载地址:http://yehbm.baihongyu.com/

你可能感兴趣的文章
大白话Vue源码系列(02):编译器初探
查看>>
[Sdoi2016]平凡的骰子
查看>>
mysql定义游标
查看>>
两个有序数组合并算法
查看>>
面向对象设计原则之五:迪米特法则
查看>>
GitHub for Windows简单使用
查看>>
c#操作XML
查看>>
作为一个测试leader平时应该注意哪些方面
查看>>
【DOM编程艺术】Ajax(Hijax)
查看>>
微信公众平台开发(十) 消息回复总结——用其xml模板
查看>>
iOS.CM5.CM4.CM2
查看>>
菜鸟学T-SQL---------SQL2005读书笔记1
查看>>
Python--函数(全局变量和局部变量)
查看>>
PLSQL Developer 不能连接 oracle 11g 64位 的解决办法
查看>>
byobu相关操作
查看>>
父页面操作嵌套iframe子页面的HTML标签元素
查看>>
在TSQL中用case,when之类同时查多种情况
查看>>
Math,random()返回区间内的随机数
查看>>
TCP/IP网络协议的通俗理解,socket,http,soap
查看>>
简单数论
查看>>