《JavaScript 设计模式与开发实践》读书笔记
DOM 的发布-订阅
document.body.addEventListener(
'click',
function () {
alert(2)
},
false
) // 订阅
// 模拟用户点击
document.body.click() // 发布多个订阅
document.body.addEventListener(
'click',
function () {
alert(2)
},
false
)
document.body.addEventListener(
'click',
function () {
// 112 第8 章 发布订阅模式
alert(3)
},
false
)
document.body.addEventListener(
'click',
function () {
alert(4)
},
false
) // 多个订阅
// 模拟用户点击
document.body.click() // 发布步骤:
[1]指定发布者。
[2]给发布者添加缓存列表,存放 fn,通知订阅者。
[3]发布者->遍历缓存列表,依次触发存放的订阅者回调函数 fn(挨个发短信),发布消息。
var salesOffices = {} // 定义售楼处
salesOffices.clientList = [] // 缓存列表,存放订阅者的回调函数
salesOffices.listen = function (fn) {
// 增加订阅者
this.clientList.push(fn) // 订阅的消息添加进缓存列表
}
salesOffices.trigger = function () {
// 发布消息
for (var i = 0, fn; (fn = this.clientList[i++]); ) {
fn.apply(this, arguments) // (2) // arguments 是发布消息时带上的参数
}
}
//下面我们来进行一些简单的测试:
salesOffices.listen(function (price, squareMeter) {
// 小明订阅消息
console.log('价格= ' + price)
console.log('squareMeter= ' + squareMeter)
})
salesOffices.listen(function (price, squareMeter) {
// 小红订阅消息
console.log('价格= ' + price)
console.log('squareMeter= ' + squareMeter)
})
salesOffices.trigger(2000000, 88) // 输出:200 万,88 平方米
salesOffices.trigger(3000000, 110) // 输出:300 万,110 平方米var salesOffices = {} // 定义售楼处
salesOffices.clientList = [] // 缓存列表,存放订阅者的回调函数
salesOffices.listen = function (key, fn) {
if (!this.clientList[key]) {
// 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
this.clientList[key] = []
}
this.clientList[key].push(fn) // 订阅的消息添加进消息缓存列表
}
salesOffices.trigger = function () {
// 发布消息
var key = Array.prototype.shift.call(arguments), // 取出消息类型
fns = this.clientList[key] // 取出该消息对应的回调函数集合
if (!fns || fns.length === 0) {
// 如果没有订阅该消息,则返回
return false
}
for (var i = 0, fn; (fn = fns[i++]); ) {
fn.apply(this, arguments) // (2) // arguments 是发布消息时附送的参数
}
}
salesOffices.listen('squareMeter88', function (price) {
// 小明订阅88 平方米房子的消息
console.log('价格= ' + price) // 输出: 2000000
})
salesOffices.listen('squareMeter110', function (price) {
// 小红订阅110 平方米房子的消息
console.log('价格= ' + price) // 输出: 3000000
})
salesOffices.trigger('squareMeter88', 2000000) // 发布88 平方米房子的价格
salesOffices.trigger('squareMeter110', 3000000) // 发布110 平方米房子的价格//所以我们把发布—订阅的功能提取出来,放在一个单独的对象内:
var event = {
clientList: [],
listen: function (key, fn) {
if (!this.clientList[key]) {
this.clientList[key] = []
}
this.clientList[key].push(fn) // 订阅的消息添加进缓存列表
},
trigger: function () {
var key = Array.prototype.shift.call(arguments), // (1);
fns = this.clientList[key]
if (!fns || fns.length === 0) {
// 如果没有绑定对应的消息
return false
}
for (var i = 0, fn; (fn = fns[i++]); ) {
fn.apply(this, arguments) // (2) // arguments 是trigger 时带上的参数
}
}
}var installEvent = function (obj) {
for (var i in event) {
obj[i] = event[i]
}
} // 给对象 `obj` 动态安装发布-订阅功能//再来测试一番,我们给售楼处对象salesOffices 动态增加发布—订阅功能:
var salesOffices = {}
installEvent(salesOffices)
salesOffices.listen('squareMeter88', function (price) {
// 小明订阅消息
console.log('价格= ' + price)
})
salesOffices.listen('squareMeter100', function (price) {
// 小红订阅消息
console.log('价格= ' + price)
})
salesOffices.trigger('squareMeter88', 2000000) // 输出:2000000
salesOffices.trigger('squareMeter100', 3000000) // 输出:3000000小明突然不想买房子了,避免继续接收短信,TD。
event.remove = function (key, fn) {
var fns = this.clientList[key]
if (!fns) {
// 如果key 对应的消息没有被人订阅,则直接返回
return false
}
if (!fn) {
// 如果没有传入具体的回调函数,表示需要取消key 对应消息的所有订阅
fns && (fns.length = 0)
} else {
for (var l = fns.length - 1; l >= 0; l--) {
// 反向遍历订阅的回调函数列表
var _fn = fns[l]
if (_fn === fn) {
fns.splice(l, 1) // 删除订阅者的回调函数
}
}
}
}var salesOffices = {}
var installEvent = function (obj) {
for (var i in event) {
obj[i] = event[i]
}
} // 给对象 `obj` 动态安装发布-订阅功能
installEvent(salesOffices) // 给对象 `salesOffices` 动态增加发布-订阅功能salesOffices.listen(
'squareMeter88',
(fn1 = function (price) {
// 小明订阅消息
console.log('价格= ' + price)
})
)
salesOffices.listen(
'squareMeter88',
(fn2 = function (price) {
// 小红订阅消息
console.log('价格= ' + price)
})
) // 多个订阅
salesOffices.remove('squareMeter88', fn1) // 删除小明的订阅
salesOffices.trigger('squareMeter88', 2000000) // 输出:2000000