《javascript设计模式与开发实践》读书笔记(4)
本次笔记记录本周学习的迭代器模式和发布-订阅模式。
迭代器模式
定义:提供一种方式顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式把迭代的过程分离出来,不用关系对象内部构造。javascript中的forEach就是一个迭代器。
一种迭代器的实现
var each = function(arr, callback){
for(var i = 0, l = arr.length; i < l; i++){
callback.call(arr, i, arr[i])
}
}
each([1,2,3], function(i, n){
alert(i,n)
}) 内部迭代器和外部迭代器
内部迭代器就如上面的代码,each函数内部定义好了迭代规则,外部一次调用,就会完成整个迭代过程。外部迭代器要求必须显示的请求迭代下一个元素,这增加了调用的复杂度,但增加了迭代器的灵活性,可以控制迭代过程。如下是外部迭代器的实现:
var Iterator = function(obj){
var current = 0
var next = function(){
current += 1
}
var isDone = function(){
return current >= obj.length
}
var getCurrItem = function(){
return obj[current]
}
return {
next: next,
isDone: isDone,
getCurrItem: getCurrItem,
length: obj.length
}
} 必须显示的调用iterator.next()来进行下一个元素的迭代。
迭代类数组对象和字面量对象
例如jquery中的each函数:
$.each = function(obj, callback){
var value, i = 0, length = obj.length, isArray = isArraylike(obj)
if(isArray){
for(; i < length; i++){
value = callback.call(obj, i, obj[i])
if(value === false){
break
}
}
}else{
for(i in obj){
value = callback.call(obj, i, obj[i])
if(value === false){
break
}
}
}
return obj
} 总结
迭代器模式很容易理解,就是将迭代与业务逻辑分开。
发布-订阅模式
定义:又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生该拜年时,所有依赖于它的对象都将得到通知。在js中,我们一般用事件模型来替代发布-订阅模式。
发布-订阅模式让两个对象松耦合地联系在一起,在新订阅者出现时,发布者的代码不需要任何修改;发布者需要改变时,也不会影戏之前的订阅者。
js中DOM事件就用到的发布-订阅模式。下面的代码就是订阅了body上的click事件,当body被点击后,会向订阅者发布这个消息。
document.body.addEventListener('click', fucntion(){
alert(2)
})
document.body.click() 自定义事件
思路:定义发布者,把回调函数存放在发布者中的缓存列表,当发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的回调函数。
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)
}
}
salesOffices.listen('squareMeter88', function(price){
console.log('price: ' + price)
})
salesOffices.trigger('squareMeter88', 20000) 取消订阅事件
如果要取消已经订阅的事件,需要在发布者对象中添加remove方法,用来取消订阅,接着上面的例子,代码如下:
salesOffices.remove = function(key, fn){
var fns = this.clientList[key]
if(!fns || fns.length === 0){
return false
}
if(!fn){
fns && (fns.length = 0) //如果没有具体回调函数,则取消所有订阅消息
}else{
for(var l = fns.length - 1; l >= 0; l--){
if(fns[l] === fn){
fns.splice(l, 1) //反向遍历,用splice不会出问题
}
}
}
} 先发布再订阅
类似于离线消息,效果如下:
Event.trigger('click', 1)
Event.listen('click', function(a){
console.log(a)
}) 具体实现思路是建立一个存放离线事件的堆栈,在事件发布的时候,如果还没有订阅这个事件,则暂时把这些动作包装在一个函数中,把包装函数存入堆栈,等到有对象来定于此事件,就便利堆栈并且依次执行这些包装函数。这里的代码比较复杂,包括解决全局命名冲突的问题,后续仔细研究。
总结
发布-订阅模式对对象之间进行了解耦,使发布者和订阅者逻辑分开,但是如果过度使用,对象与对象之间的必要联系会被深埋在背后,导致程序难以跟踪维护。并且发布-订阅者模式会消耗一定的内存和时间。
#笔记##读书笔记##设计#
凡岛公司福利 824人发布