本系列文章主要根据《JavaScript设计模式与开发实践》整理而来,其中会加入了一些本身的思考。但愿对你们有所帮助。ajax
js设计模式--单例模式segmentfault
js设计模式--策略模式设计模式
发布—订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状 态发生改变时,全部依赖于它的对象都将获得通知。this
document.body.addEventListener('click', function () { alert(2); }, false); document.body.addEventListener('click', function () { alert(3); }, false); document.body.addEventListener('click', function () { alert(4); }, false); document.body.click(); // 模拟用户点击
优势:发布—订阅模式的优势很是明显,一为时间上的解耦,二为对象之间的解耦。
缺点:建立订阅者自己要消耗必定的时间和内存,而 且当你订阅一个消息后,也许此消息最后都未发生,但这个订阅者会始终存在于内存中。prototype
var Event = function() { this.list = [] } Event.prototype.add = function(listener) { this.list.push(listener) } Event.prototype.triggle = function() { this.list.forEach(listener => { listener() }) } var event = new Event() event.add(()=>{console.log('房源1--80平--200万')}) event.add(()=>{console.log('房源2--200平--1000万')}) event.triggle()
或者设计
var event = { list: [], add(listener) { this.list.push(listener) }, triggle() { this.list.forEach(listener => { listener() }) } } event.add(()=>{console.log('房源1--80平--200万')}) event.add(()=>{console.log('房源2--200平--1000万')}) event.triggle()
但这种不能区分是发不了什么消息,好比有两群人:订阅80平房源报价和订阅200瓶房源报价的人,这里两群人都会获得通知代理
var event = { list: {}, add(type, listener) { if (!this.list[type]) { this.list[type] = [] } this.list[type].push(listener) }, triggle(type) { this.list[type] && this.list[type].forEach(listener => { listener() }) } } event.add('80平', ()=>{console.log('房源1--80平--200万')}) event.add('80平', ()=>{console.log('房源2--80平--300万')}) event.add('200平', ()=>{console.log('房源2--200平--1000万')}) event.triggle('80平')
这里还少了一个取消订阅的功能code
var event = { list: {}, add(type, listener) { if (!this.list[type]) { this.list[type] = [] } this.list[type].push(listener) }, triggle(type) { this.list[type] && this.list[type].forEach(listener => { listener() }) }, remove(type, fn) { if (!this.list[type]) return var index = this.list[type].findIndex(listener => listener === fn) this.list[type].splice(index, 1) } } var f1 = ()=>{console.log('房源1--80平--200万')} var f2 = ()=>{console.log('房源2--80平--300万')} var f3 = ()=>{console.log('房源2--200平--1000万')} event.add('80平', f1) event.add('80平', f2) event.add('200平', f3) event.remove('80平', f2) event.triggle('80平') // 房源1--80平--200万
上面代码结构还不是很清晰,咱们再模拟销售部真实的场景
var Event = function () { this.list = {} } Event.prototype.add = function (area, client) { if (!this.list[area]) this.list[area] = [] this.list[area].push(client) } Event.prototype.remove = function (area, client) { if (!this.list[area]) return var index = this.list[area].findIndex(item => item === client) this.list[area].splice(index, 1) } Event.prototype.triggle = function (area, price) { if (!this.list[area]) return this.list[area].forEach(client => { client.listen(area, price) }) } var Client = function (name) { this.name = name } Client.prototype.listen = function (area, price) { console.log(`${this.name}收到${area}平的房源报价${price}`) } var client1 = new Client('client1') var client2 = new Client('client2') var event = new Event() event.add('80平', client1) event.add('100平', client1) event.add('80平', client2) event.add('300平', client1) event.remove('300平', client1) event.triggle('80平', 200) // client1收到80平平的房源报价200 client2收到80平平的房源报价200 event.triggle('100平', 500) // client1收到100平平的房源报价500 event.triggle('200平', 1000) // event.triggle('300平', 1000) //
上面的代码虽然已经很好了,可是仍是有一个缺点:订阅者接收不到订阅以前发布的消息,以下客户3也想订阅80平的房源,但他收不到任何消息
var client3 = new Client('client3') event.add('80平', client3)
咱们但愿客户3也能收到消息
咱们增长一个cache字段来记录历史房源报价
var Event = function () { this.list = {} this.cache = {} } Event.prototype.add = function (area, client) { if (!this.list[area]) this.list[area] = [] this.list[area].push(client) this.cache[area].forEach(price => { client.listen(area, price) }) } Event.prototype.remove = function (area, client) { if (!this.list[area]) return var index = this.list[area].findIndex(item => item === client) this.list[area].splice(index, 1) } Event.prototype.triggle = function (area, price) { if (!this.cache[area]) this.cache[area] = [] this.cache[area].push(price) if (!this.list[area]) return this.list[area].forEach(client => { client.listen(area, price) }) } var Client = function (name) { this.name = name } Client.prototype.listen = function (area, price) { console.log(`${this.name}收到${area}平的房源报价${price}`) } var client1 = new Client('client1') var client2 = new Client('client2') var event = new Event() // event.add('80平', client1) // event.add('100平', client1) // event.add('80平', client2) // event.add('300平', client1) // event.remove('300平', client1) event.triggle('80平', 200) // client1收到80平平的房源报价200 client2收到80平平的房源报价200 event.triggle('100平', 500) // client1收到100平平的房源报价500 event.triggle('200平', 1000) // event.triggle('300平', 1000) // var client3 = new Client('client3') event.add('80平', client3) event.add('100平', client3)
假如咱们正在开发一个商城网站,网站里有 header 头部、nav 导航、消息列表、购物车等模块。这几个模块的渲染有一个共同的前提条件,就是必须先用 ajax 异步请求获取用户的登陆信息。
这里留给读者本身实现