今天看啥  ›  专栏  ›  _able

订阅-发布模式 && 回调 && 钩子函数

_able  · 掘金  ·  · 2020-02-28 07:59
    

文章预览

阅读 23

订阅-发布模式 && 回调 && 钩子函数

订阅-发布模式 && 回调 && 钩子函数

《JavaScript设计模式于开发实践》曾探,读书笔记

发布-订阅模式又叫做观察者模式,它定义了对象间一种一对多的关系,当一个对象发生改变的时候,所有依赖于它的对象都将得到通知。

发布—订阅模式可以广泛应用于异步编程中,这是一种替代传递回调函数的方案。

从售楼的例子讲解回调于订阅-发布

小明有了钱想买房子所以来到了售楼部,售楼部小姐姐给小明说现有的房子已经没了。马上有一处的楼盘要开通了,但是具体是什么时候小明不知道。

  • 第一种情况,以后小明每天都会打电话给售楼部,问楼盘情况。主动权在小明处,小明有权决定什么时候打电话(执行打电话这个回调,然后得到楼盘情况)。
  • 小姐姐留下了小明的电话,然后当楼盘可以出售,然后给小明打电话。(主动权在售楼部)

dom事件

在dom上绑定事件就是一个订阅-发布的模式。

自定义事件

  1. 指定发布者(售楼部)
  2. 发布者设置缓存列表,用于存放回调函数方便通知订阅者(售楼部的花名册)
  3. 发布消息的时候,遍历花名册,通知到每个人。

下面是一个售楼部通知客户的简单实现

// 售楼部
var salesOffice = {}
// 用户短信名单
salesOffice.clientList = []
// 订阅事件
salesOffice.listen = function(fn) {
    this.clientList.push(fn)
}
// 发布消息
salesOffice.trigger = function() {
    for(var i = 0, fn; fn = this.clientList[i++];) {
        fn.apply(this, arguments)
    }
} 

// 用户订阅消息

salesOffice.listen(function(price) {
    console.log('用户1', price)
})

salesOffice.listen(function(price) {
    console.log('用户2', price)
})

salesOffice.trigger(120000)
复制代码
  • 以上代码有个问题,没有实现我们的选择性订阅,而是一股脑的把所有消息给了用户。
  • 如果这个售楼部又A,B两个楼盘,那么怎么实现选择性的定于了
// 售楼部
var salesOffice = {}
// 用户短信名单
salesOffice.clientList = {}
// 订阅事件,使用key来丝线自定义订阅
salesOffice.listen = function(key,fn) {
    if(!this.clientList[key]) this.clientList[key] = []
    this.clientList[key].push(fn)
}
// 发布消息,使用key来发布特定消息
salesOffice.trigger = function(key) {
    for(var i = 0, fn; fn = this.clientList[key][i++];) {
        // 去掉第一个参数
        let args = Array.from(arguments)
        args.shift()
        fn.apply(this, args)
    }
} 

// 用户订阅消息

// 订阅了A楼盘的信息
salesOffice.listen('A',function(price, size) {
    console.log('用户1', price, size)
})

// 订阅了B楼盘的信息
salesOffice.listen('B',function(price, size) {
    console.log('用户2', price, size)
})

salesOffice.trigger('B',120000, 80)
复制代码

上面代码是不是于dom事件绑定十分相似了

document.body.addEventListener('click', function(){
    console.log('click')
})
复制代码

通用的订阅-发布

以上就是比较完整的订阅-发布的功能。但是我们还可以更加通用。如果又另一个售楼部又要实现这个功能,就需要把代码重新写一遍。所以我们需要把他变得更加通用。

// 抽离订阅-发布功能
var event = {
    clientList: {},
    listen: function(key,fn) {
        if(!this.clientList[key]) this.clientList[key] = []
        this.clientList[key].push(fn)
    },
    trigger: function(key) {
        for(var i = 0, fn; fn = this.clientList[key][i++];) {
            // 去掉第一个参数
            let args = Array.from(arguments)
            args.shift()
            fn.apply(this, args)
        }
    } 
}

// 不同的售楼部 并且安装 订阅-发布功能
var salesOfficeA = {...event} // 售楼部A
var salesOfficeB = {...event} // 售楼部B

// 用户A 订阅 售楼部A的A楼盘
salesOfficeA.listen('A', function(price, size){
    console.log('用户A', price, size)
})

// 用户B 订阅 售楼部B的B楼盘
salesOfficeB.listen('B', function(price, size){
    console.log('用户B', price, size)
})

// 售楼部A发布A楼盘的信息
salesOfficeA.trigger('A', 120000, 89)
复制代码

取消订阅

event.remove: function(key, fn) {
    var fns = this.clientList[key]
    for(var i = 0; i < fns.length; i++) {
        if(fns[i] === fn) {
            fns.splice(i, 1)
        }
    }
}
// 不同的售楼部 并且安装 订阅-发布功能
var salesOfficeA = {...event} // 售楼部A
var salesOfficeB = {...event} // 售楼部B

// 用户A 订阅 售楼部A的A楼盘
let fn = function(price, size){
    console.log('用户A', price, size)
}
salesOfficeA.listen('A', fn)
复制代码

注意 订阅于取消订阅是同一个fn

实际例子对比回调于发布-订阅

有如下功能

  • 登陆
  • 登陆成功之后得到用户信息
  • 得到用户信息渲染header
  • 渲染conent内容板块
  1. 第一种使用回调的方式写法如下:
login.succ(function(data){
    header.setHeader(data)
    conent.setContent(data)
})
复制代码

这种方式是回调的方式,不利于代码扩展。以后如果foot又徐哟啊渲染,还得回到这个地方重新修改。

  1. 发布-订阅的方式
$.ajax(url, function(data) => {
    // 发布登陆成功的消息
    login.trigger(data)
})

var header = (function(){
    // 订阅
    login.listen('succ', function(data) => {
        header.setHeader(data)
    })
    return {
        setHeader: function(data) {
            // 具体渲染header的代码
        }
    }
})()
复制代码

通过对比发布-订阅方式的耦合性没那么强。

钩子函数

我们常常说的钩子函数就是通过发布-订阅的方式实现的。他于回调的区别已经对比以上已经对其有所说明了

………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览