前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >javascript设计模式六:发布-订阅模式(观察者模式)

javascript设计模式六:发布-订阅模式(观察者模式)

作者头像
前端_AWhile
发布2019-08-29 13:15:30
6590
发布2019-08-29 13:15:30
举报
文章被收录于专栏:前端一会前端一会

发布-订阅模式也叫观察者模式,是js开发中应用广泛的一种模式。下面将列举一个通用发布订阅模式的示例,应用到闭包、this、apply/call、自执行函数等概念,起码达到熟悉的程度,才有可能把发布-订阅模式真正吃透并能灵活运用到实际场景中去。

常见的发布订阅模式应用场景有:登录后head/nav等模块异步获取登录成功返回的数据;页面无刷新点击事件进行数据自增…

代码语言:javascript
复制
 1var ObserverEvent = (function(){
 2    var cacheList = {},     //缓存列表,存放订阅者的回调函数
 3        listen,             //添加订阅方法
 4        trigger,            //发布消息
 5        remove;             //取消订阅方法
 6
 7    listen = function(key, fn){ 
 8        if(!cacheList[key]){    //如果还没有订阅过此类消息,给该类消息创建一个缓存列表
 9            cacheList[key]=[]   
10        }
11
12        cacheList[key].push(fn)     //订阅的消息添加进消息缓存列表
13    };
14
15    trigger = function(){       
16        var key = Array.prototype.shift.call(arguments),     //取出消息类型
17            fns = cacheList[key];       //取出该消息对应的回调函数集合
18
19        if(!fns || fns.length==0){      //如果没有订阅该消息,则返回
20            return false
21        }
22
23        //forEach参数用es5函数写法时,注意要将外层arguments对象预先保存引用再传进行该参数中
24        //var restParameters = arguments  //将截取首位元素后arguments赋值给新的变量引用,供传递如下
25        // fns.forEach(function(fn, i){
26        //     fn.apply(this, restParameters)
27        // })
28
29        //forEach用es6函数写法时,由于es6的箭头函数不暴露arguments对象,所以可以在箭头函数中使用arguments,因为它指向的是其外层函数中的arguments对象。
30        // fns.forEach((fn)=>{     
31        //     fn.apply(this, arguments)
32        // })
33
34        //最基本也最保险可以使用for循环遍历的写法
35        for(var i=0; i<fns.length; i++){
36            //arguments是发布消息时传入的参数,由于arguments已经由shift()方法截取出首位数值并保留剩余数值,所以传入的参数为剩余的数值
37            //这里知识点: shitf()方法删除数组的第一个元素,并返回第一个元素,且该方法直接操作原数组,也就是原数组已被修改
38            fns[i].apply(this, arguments)
39        }
40    };
41
42    remove = function(key, fn){
43        var fns = cacheList[key]
44
45        if(!fns || fns.length==0){      //如果key对应的消息没有被订阅,则直接返回
46            return false
47        }
48
49        if(!fn){    //如果没有传入具体的回调函数,则表示需要取消该key对应消息的所有订阅
50            fns.length=0
51        }else {
52            for(var l=fns.length-1; l>=0; l--){
53                var _fn = fns[l]
54                if(_fn == fn){
55                    fns.splice(l, 1)
56                }
57            }
58        }
59    };
60
61    return {
62        cacheList,
63        listen,
64        trigger,
65        remove
66    }
67})()
68
69
70//定义发布对象安装函数,这个函数可以给所有的对象动态安装发布-订阅功能
71let installEvent = function(obj){
72    for(var i in ObserverEvent){
73        obj[i] = ObserverEvent[i]
74    }
75}
76
77//创建需要发布功能的某个对象
78var loginModel = {};
79installEvent(loginModel)    //为该对象装载发布订阅功能
80
81
82
83loginModel.listen('loginSucc', function(price){
84    console.log(price);
85})
86
87setTimeout(function(){
88    loginModel.trigger('loginSucc', 2000)
89}, 3000)
90
91//打印结果
92//3秒后打印:
93//2000

发布订阅模式可以为模块间通信提供连接桥梁,沿用上例的全局发布订阅模块,示例如下:

代码语言:javascript
复制
 1<!DOCTYPE html>
 2<html lang="en">
 3<head>
 4    <meta charset="UTF-8">
 5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 6    <meta http-equiv="X-UA-Compatible" content="ie=edge">
 7    <title>模块间通信</title>
 8</head>
 9<body>
10    <div id="btn1">点我</div>
11    <div id="show">0</div>
12</body>
13<script>
14    var a = (function(){
15        var count = 0
16        document.getElementById('btn1').onclick = function(){
17            ObserverEvent.trigger('add', ++count)
18            // Event.remove('add')
19        }
20    })()
21
22    var b = (function(){
23        ObserverEvent.listen('add', function(count){
24            document.getElementById('show').innerHTML = count
25        })
26    })()
27
28    //模块a和模块b之间通信,模块b监听add回调函数,模块a通过点击事件触发add回调函数
29</script>
30</html>

最后插个闭包的相关吧。

怎么理解面向对象中的对象呢?对象是过程和数据的结合,对象以方法的形式包含了过程,在方法中可以用this访问到所处对象环境中的数据,以供方法中的过程使用。

怎么理解闭包呢?闭包在过程中以上下文环境的形式包含了数据,即闭包始终保持对上下文环境中数据的引用。

发现共同点了么?

对象过程与闭包过程都能始终保持对所处上下文环境数据的引用。

show代码:

代码语言:javascript
复制
 1//闭包
 2var count = function(){
 3    var val = 0;
 4    return function(){
 5        console.log(val)
 6        val++
 7    }
 8}
 9var calcute = count()
10calcute()   //0
11calcute()   //1
12calcute()   //2
13
14//面向对象
15var count = {
16    val: 0,
17    add: function(){
18        console.log(this.val)
19        this.val++
20    }
21}
22count.add() //0
23count.add() //1
24count.add() //2
25
26//或者用构造函数写面向对象
27var Count = function(val){
28    this.val = val
29}
30Count.prototype.add = function(){
31    console.log(this.val)
32    this.val++
33}
34var c1 = new Count(0)
35c1.add()    //0
36c1.add()    //1 
37c1.add()    //2
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-11-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端小二 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档