发布-订阅模式也叫观察者模式,是js开发中应用广泛的一种模式。下面将列举一个通用发布订阅模式的示例,应用到闭包、this、apply/call、自执行函数等概念,起码达到熟悉的程度,才能说把发布-订阅模式真正吃透并能灵活运用到实际场景中去。
常见的发布订阅模式应用场景有:登录后head/nav等模块异步获取登录成功返回的数据;页面无刷新点击事件进行数据自增…
var ObserverEvent = (function(){
var cacheList = {}, //缓存列表,存放订阅者的回调函数
listen, //添加订阅方法
trigger, //发布消息
remove; //取消订阅方法
listen = function(key, fn){
if(!cacheList[key]){ //如果还没有订阅过此类消息,给该类消息创建一个缓存列表
cacheList[key]=[]
}
cacheList[key].push(fn) //订阅的消息添加进消息缓存列表
};
trigger = function(){
var key = Array.prototype.shift.call(arguments), //取出消息类型
fns = cacheList[key]; //取出该消息对应的回调函数集合
if(!fns || fns.length==0){ //如果没有订阅该消息,则返回
return false
}
//forEach参数用es5函数写法时,注意要将外层arguments对象预先保存引用再传进行该参数中
//var restParameters = arguments //将截取首位元素后arguments赋值给新的变量引用,供传递如下
// fns.forEach(function(fn, i){
// fn.apply(this, restParameters)
// })
//forEach用es6函数写法时,由于es6的箭头函数不暴露arguments对象,所以可以在箭头函数中使用arguments,因为它指向的是其外层函数中的arguments对象。
// fns.forEach((fn)=>{
// fn.apply(this, arguments)
// })
//最基本也最保险可以使用for循环遍历的写法
for(var i=0; i<fns.length; i++){
//arguments是发布消息时传入的参数,由于arguments已经由shift()方法截取出首位数值并保留剩余数值,所以传入的参数为剩余的数值
//这里知识点: shitf()方法删除数组的第一个元素,并返回第一个元素,且该方法直接操作原数组,也就是原数组已被修改
fns.apply(this, arguments)
}
};
remove = function(key, fn){
var fns = cacheList[key]
if(!fns || fns.length==0){ //如果key对应的消息没有被订阅,则直接返回
return false
}
if(!fn){ //如果没有传入具体的回调函数,则表示需要取消该key对应消息的所有订阅
fns.length=0
}else {
for(var l=fns.length-1; l>=0; l--){
var _fn = fns[l]
if(_fn == fn){
fns.splice(l, 1)
}
}
}
};
return {
cacheList,
listen,
trigger,
remove
}
})()
//定义发布对象安装函数,这个函数可以给所有的对象动态安装发布-订阅功能
let installEvent = function(obj){
for(var i in ObserverEvent){
obj = ObserverEvent
}
}
//创建需要发布功能的某个对象
var loginModel = {};
installEvent(loginModel) //为该对象装载发布订阅功能
loginModel.listen('loginSucc', function(price){
console.log(price);
})
setTimeout(function(){
loginModel.trigger('loginSucc', 2000)
}, 3000)
//打印结果
//3秒后打印:
//2000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
发布订阅模式可以为模块间通信提供连接桥梁,沿用上例的全局发布订阅模块,示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>模块间通信</title>
</head>
<body>
<div id="btn1">点我</div>
<div id="show">0</div>
</body>
<script>
var a = (function(){
var count = 0
document.getElementById('btn1').onclick = function(){
ObserverEvent.trigger('add', ++count)
// Event.remove('add')
}
})()
var b = (function(){
ObserverEvent.listen('add', function(count){
document.getElementById('show').innerHTML = count
})
})()
//模块a和模块b之间通信,模块b监听add回调函数,模块a通过点击事件触发add回调函数
</script>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
最后插个闭包的相关吧。
怎么理解面向对象中的对象呢?对象是过程和数据的结合,对象以方法的形式包含了过程,在方法中可以用this访问到所处对象环境中的数据,以供方法中的过程使用。
怎么理解闭包呢?闭包在过程中以上下文环境的形式包含了数据,即闭包始终保持对上下文环境中数据的引用。
发现共同点了么?
对象过程与闭包过程都能始终保持对所处上下文环境数据的引用。
show代码:
//闭包
var count = function(){
var val = 0;
return function(){
console.log(val)
val++
}
}
var calcute = count()
calcute() //0
calcute() //1
calcute() //2
//面向对象
var count = {
val: 0,
add: function(){
console.log(this.val)
this.val++
}
}
count.add() //0
count.add() //1
count.add() //2
//或者用构造函数写面向对象
var Count = function(val){
this.val = val
}
Count.prototype.add = function(){
console.log(this.val)
this.val++
}
var c1 = new Count(0)
c1.add() //0
c1.add() //1
c1.add() //2
---------------------
【转载,仅作分享,侵删】
作者: 一期一会
原文:https://blog.csdn.net/qq_34832846/article/details/85989578
|
|