daily-share icon indicating copy to clipboard operation
daily-share copied to clipboard

简单的了解发布与订阅模式(2020-1-19)

Open yaogengzhu opened this issue 5 years ago • 6 comments

简单的了解发布与订阅模式(2020-1-19)

简单的了解发布订阅模式,简单来说,发布订阅模式也是观察者模式。

  var salesOffices = {} 
        salesOffices.clientLis = [] // 存放订阅的函数
        salesOffices.listen = function(fn) { // 添加订阅者
            this.clientLis.push(fn)
        }

        salesOffices.trigger = function() { // 发布消息
            for(var i =0, fn; fn = this.clientLis[i++]; ) {
                fn.apply(this, arguments)
            }
        }

简单的模式

yaogengzhu avatar Jan 19 '20 07:01 yaogengzhu

简单的发布订阅模式

// 实现简单的订阅模式
        var salesOffice = {}  // 定义售楼处
        salesOffice.clientList = []  // 缓存列表,用于存放订阅着的回调函数
    
        salesOffice.listen = function(key, fn) {
            if (!this.clientList[key]) { // 如果还没有订阅过此类消息,给该类消息创建一个缓存列表
                this.clientList[key] = []  // 初始化
            }
            this.clientList[key].push(fn) // 订阅的消息添加进缓存列表中
        }     
        salesOffice.trigger = function () { // 发布消息
            var key = Array.prototype.shift.call(arguments)  // 取出消息类型
            var fns = this.clientList[key]  // 取出该消息的对应的回调函数

            if(!fns || fns.length === 0) {  // 如果没有订阅过该消息直接返回
                return false
            }

            for(var i=0, fn; fn = fns[i++]; ) {
                fn.apply(this, arguments)  
            }

        }
        // test 
        salesOffice.listen('squareMeter88', function(price) {
            console.log('价格= ' + price)
        })
        salesOffice.listen('squareMeter88', function(price) {
            console.log('价格= ' + price)
        })

        salesOffice.listen('squareMeter90', function(price) {
            console.log('价格= ' + price)
        })
        salesOffice.trigger('squareMeter88', 88)
        salesOffice.trigger('squareMeter90', 90)

yaogengzhu avatar Jan 19 '20 08:01 yaogengzhu

 var event = {
            list: [], // 用来存放缓存的订阅函数
            listen: function(key, fn) {
                if (!this.list[key]) {
                    this.list[key] = [] // 初始化一个数组
                }
                this.list[key].push(fn)
            },
            trigger: function() {
                var key = Array.prototype.shift.call(arguments) // 取出参数中的第一kye
                console.log(key)
                var fns = this.list[key] // 拿出定订阅的缓存函数

                if (!fns || fns.length === 0) return false  // 判断是否存key值对应的缓存函数,不存在的话终止执行
                this.list.forEach((fn) => {  // 循环自调用每一个函数
                    fn.apply(this, arguments)
                })
            }
        }

yaogengzhu avatar Jan 19 '20 09:01 yaogengzhu

        var event = {
            list: [], // 用来存放缓存的订阅函数
            listen: function(key, fn) {
                if (!this.list[key]) {
                    this.list[key] = [] // 初始化一个数组
                }
                this.list[key].push(fn)
            },
            trigger: function() {
                var key = Array.prototype.shift.call(arguments) // 取出参数中的第一kye
                console.log(key)
                var fns = this.list[key] // 拿出定订阅的缓存函数

                if (!fns || fns.length === 0) return false  // 判断是否存key值对应的缓存函数,不存在的话终止执行
                this.list.forEach((fn) => {  // 循环自调用每一个函数
                    fn.apply(this, arguments)
                })
            }
        }

        function installEvent(obj) {
            for (var key in event ) {
                obj[key] = event[key]
            }
        }

yaogengzhu avatar Jan 19 '20 10:01 yaogengzhu

完整的发布订阅模式

// 手写一个发布订阅
        var event = {
            list: [], // 用来存储订阅的函数
            listen: function(key, fn) {
                if (!this.list[key]) {
                    this.list[key] = []
                } 
                if (!fn) {
                    return false // 如果没有传入具体的回到函数,则直接返回false
                }
                this.list[key].push(fn)
            },
            trigger: function() {
                // 
                var key = Array.prototype.shift.call(arguments)
                var fns = this.list[key]

                if (!fns || fns.length === 0) return false 
                fns.forEach( (fn) => {
                    fn.apply(this, arguments)
                })
            },
            remove: function(key, fn) {
                var fns = this.list[key]
                if(!fns) {  // 如果key对应的消息被人订阅,则直接返回
                    return false
                }
                if (!fn) { // 如果没有具体的传入具体的回调函数,则表示需要取消所有的key对应的消息所有订阅
                    console.log('执行')
                    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) // 删除订阅者的回调函数
                        }
                    }
                }
            }
        }

        function installEvent(obj) {
            for(var key in event ) {
                obj[key] = event[key]
            }
        }

yaogengzhu avatar Jan 20 '20 07:01 yaogengzhu

全局模式下的发布订阅

var Event = (function() {
            var clientList = {},
                listen,
                trigger,
                remove

            listen = function(key, fn) {
                if (!clientList[key]) {
                    clientList[key] = []
                }
                clientList[key].push(fn)
            }

            trigger = function() {
                var key = Array.prototype.shift.call(arguments)
                var fns = clientList[key]
                if (!fns || fns.length === 0 ) {
                    return false
                }

                // 循环遍历执行代码
                fns.forEach( (fn) => {
                    fn.apply(this, arguments)
                })
            }

            remove = function(key, fn) {
                var fns = clientList[key]
                if (!fns) {  // 如果传入的key值中,没有具体的函数,直接返回
                    return false
                } 
                if (!fn) { // 如果没有传入具体的回调函数,清空所有的key对应的函数
                    fns && (fns.length = 0)
                } else {
                    // 存在fn ,需要找到这个fn,然后删除
                    for (var i = fns.length - 1; i >=0; i--) {
                        var _fn = fns[i]
                        if (_fn === fn) {
                            fns.splice(i, 1)
                        }
                    }
                }
            }

            return {
                clientList: clientList,
                listen: listen,
                trigger: trigger,
                remove: remove
            }
        })()

yaogengzhu avatar Jan 20 '20 08:01 yaogengzhu

ts 版本


// 发布订阅

class BusEvent {
    public type: string
    public fn: (...arg: any[]) => void
    constructor(type: string, fn: (...args: any[]) => void) {
        this.type = type
        this.fn = fn
    }

}
class Listen {

    public listenrs = new Set<BusEvent>()

    public add(type: string, fn: (...args: any[]) => void) {
        const event = new BusEvent(type, fn)
        this.listenrs.add(event)

        return () => {
            this.listenrs.delete(event)
        }
    }

    public dispatch(type: string, ...arg: any[]) {
        this.listenrs.forEach( item => {
            if (item.type === type)  {
                item.fn(...arg)
            }
        })
    }

    public remove(type: string,  fn: (...args: any[]) => void) {
        const event = new BusEvent(type, fn)
        this.listenrs.delete(event)
    }
}

export const listen = new Listen()

yaogengzhu avatar Nov 07 '20 00:11 yaogengzhu