精华内容
下载资源
问答
  • JS发布订阅模式

    2020-07-21 19:37:34
    目录JS发布订阅模式一、直白的发布订阅例子二、用户只订阅自己感兴趣的消息三、发布订阅模式的通用实现四、封装成中介 - 解决代码耦合五、离线储存+命名空间 - 解决离线事件 JS发布订阅模式 一、直白的发布订阅例子 ...

    JS发布订阅模式

    一、直白的发布订阅例子

    简单的售楼处例子:

    1. 售楼处是发布者

      var salesOffices = {};
      
    2. 添加订阅者的方法,以一个函数代表一个订阅者

      /**
       * 增加订阅者
       * 
       * @param {Function} fn 发送消息给订阅者的函数
       */
      salesOffices.listen = function (fn) {
          this.clientList.push(fn)
      }
      
    3. 发布消息的方法

      salesOffices.trigger = function () {
          for (var i = 0; i < this.clientList.length; i++) {
          	fn = this.clientList[i]
              fn.apply(this, arguments)
          }
      }
      
    4. 测试

      // 添加订阅者小明
      salesOffices.listen(function (price, squareMeter) {
          console.log('price', price)
          console.log('squareMeter', squareMeter)
      })
      
      // 添加订阅者小红
      salesOffices.listen(function (price, squareMeter){
          console.log('price', price)
          console.log('squareMeter', squareMeter)
      })
      
      salesOffices.trigger(2000000, 100)
      // price 2000000
      // squareMeter 100
      // price 2000000
      // squareMeter 100
      

    二、用户只订阅自己感兴趣的消息

    1. 售楼处是发布者

      var salesOffices = {};
      
    2. 添加订阅者的方法,以一个函数代表一个订阅者

      /**
       * 增加通知订阅者
       * @param {Function} fn  发送消息给订阅者的函数
       * @param {String} event 代表某一类消息
       */
      salesOffices.listen = function (event, fn) {
      
      	// 如果还没有此类消息,给该类消息创建一个缓存列表
      	if (!this.clientList[event]) {
      		this.clientList[event] = []
      	} 
          this.clientList.push(fn)
      }
      
    3. 发布消息的方法

      /**
       * 发布消息给所有订阅此消息的人
       * 
       * @param {String} event 代表某一类消息
       * @param {*} message 发送的消息
       */
      salesOffices.trigger = function (event, message) {
      
      	// 消息标签下的订阅者
      	var subscriberFuncs = this.clientList[event]
      	
      	// 如果没有此标签或消息标签下没有订阅者
      	if (!subscriberFuncs || subscriberFuncs.length === 0) {
          	console.log('No subscribers')
          	return false
      	}
      
      	// 发布消息
          for (var i = 0, fn; fn = fns[i++]; ) {
              subscriberFuncs.apply(this, message)
          }
      }
      
    4. 测试

      // 测试:
      // 添加订阅者小明
      // 小明订阅了88平米房的信息
      salesOffices.listen('squareMeter88', function(price) {
      	console.log('price', price)
      })
      
      // 添加订阅者小红
      // 小明订阅了95平米房的信息
      salesOffices.listen('squareMeter95', function(price) {
      	console.log('price', price)
      })
      
      // 添加订阅者小李
      // 小明订阅了95平米房的信息
      salesOffices.listen('squareMeter95', function(price) {
      	console.log('price', price)
      })
      
      // 发布 88 平方米房子的价格
      salesOffices.trigger('squareMeter88', 2000000);
      // price 2000000 (通知小明)
      
      // 发布 95 平方米房子的价格
      salesOffices.trigger('squareMeter95', 2000000);
      // price 2000000 (通知小红)
      // price 2000000 (通知小李)
      
      // 发布 1000 平方米房子的价格
      salesOffices.trigger('squareMeter1000', 2000000);
      // No subscribers
      

    三、发布订阅模式的通用实现

    考虑到代码复用性,将发布—订阅的功能提取出来,放在一个单独的对象内 (Event)

    1. 定义发布者通用的基本属性和方法

      var Event = {
      	/**
      	 * 储存不同类消息下以及其下的订阅者
      	 */
          clientList: {},
      
      	/**
      	 * 增加订阅者
      	 * @param {Function} fn 发送消息给订阅者的函数
      	 */
          listen: function (event, fn) {
              if (!this.clientList[event]) {
                  this.clientList[event] = []
              }
              this.clientList[event].push(fn)
          },
      
      	/**
      	 * 发布消息给所有订阅此消息的人
      	 * @param {String} event 代表某一类消息
      	 * @param {*} message 发送的消息
      	 */
          trigger: function (event, param) {
      
              var subscriberFuncs = this.clientList[event]
      
              if (!subscriberFuncs || subscriberFuncs.length === 0) {
                  console.log('No Subscribers')
                  return false
              }
      
              for (var i = 0, fn; fn = subscriberFuncs[i++]; ) {
                  fn.apply(this, param)
              }
          },
          
          /**
      	 * 取消订阅
      	 * @param {String}   event 代表要取消的某一类消息
      	 * @param {Function} fn    代表订阅者
      	 */
      	remove: function (event, fn) {
      	    var subscriberFuncs = this.clientList[event]
      	
      	    // 如果 eventId 对应的消息没有被人订阅,则直接返回
      	    if (!subscriberFuncs) {
      	        return 
      	    }
      	
      	    // 如果没有传入具体的回调函数,表示需要取消 eventId 对应消息的所有订阅
      	    if (!fn) {
      	        subscriberFuncs.length = 0
      	        return
      	    }
      	
      	    // 取消消息 eventId 下的订阅的事件 fn
      	    for (var i = subscriberFuncs.length - 1; i >= 0; i--) {
      	        var _fn = subscriberFuncs[i]
      	        if (fn === _fn) {
      	            subscriberFuncs.splice(i, 1)
      	        }
      	    }
      	}
      };
      
    2. 让一个发布者拥有Event的属性和方法

      /**
       * 为发布者添加发布订阅模式的基本功能
       * 
       * @param {Object} obj 待添加功能的目标对象
       */
      var installEvent = function (obj) {
          for (var attr in Event) {
              obj[attr] = Event[attr]
          }
      }
      
    3. 测试

      // 测试:
      // 添加订阅者小明
      // 小明订阅了88平米房的信息
      var fnMing = function(price) {
          console.log('price', price)
      }
      salesOffices.listen('squareMeter88', fnMing)
      
      // 添加订阅者小红
      // 小明订阅了95平米房的信息
      var fnHong = function(price) {
          console.log('price', price)
      }
      salesOffices.listen('squareMeter95', fnHong)
      
      // 添加订阅者小李
      // 小明订阅了95平米房的信息
      var fnLi = function(price) {
          console.log('price', price)
      }
      salesOffices.listen('squareMeter95', fnLi)
      
      // 发布 88 平方米房子的价格
      salesOffices.trigger('squareMeter88', 2000000);
      // price 2000000 (通知小明)
      
      // 发布 95 平方米房子的价格
      salesOffices.trigger('squareMeter95', 2000000);
      // price 2000000 (通知小红)
      // price 2000000 (通知小李)
      
      
      // 小红取消订阅 squareMeter95
      salesOffices.remove('squareMeter95', fnHong)
      salesOffices.trigger('squareMeter95', 2000000);
      // price 2000000 (通知小李)
      
      // 取消所有 squareMeter88 订阅
      salesOffices.remove('squareMeter88')
      salesOffices.trigger('squareMeter88', 2000000);
      // No Subscribers
      

    四、封装成中介 - 解决代码耦合

    • Event 作为一个类似 “中介者” 的角色,把订阅者和发布者联系起来
    • Event 是一个对象,直接执行函数,内含保存订阅信息的对象clientList,返回封装好的listentriggerremove 等基本方法
    • 解决的问题是代码耦合
    var Event = (() => {
    	// 储存订阅信息的仓库
        var clientList = {}
    
    	// listen方法,负责订阅
        var listen = (event, fn) => {
            if (!clientList[event]) {
                clientList[event] = []
            }
            clientList[event].push(fn)
        }
        
    	// trigger方法,负责发布消息
    	var trigger = (event, param) => {
            var fns = clientList[event]
            if (!fns || fns.length === 0) {
                return 
            }
            for (var i = 0, fn; fn = fns[i++]; ) {
                fn.apply(this, param)
            }
        }
        
    	// remove方法,负责取消订阅
        var remove = (event, fn) => {
            var fns = clientList[event]
            if (!fns) {
                return
            }
            if (!fn) {
                fns.length === 0
                return
            }
            for (var i = 0, fn; i <= 0; i--) {
                var _fn = fns[l]
                if (_fn === fn) {
                    fns.splice(i, 1)
                }
            }
        }
    
        return { listen, trigger, remove}
    })()
    

    怎用使用?

    <html>
        <body>
            <button id="count">点击</button>
            <div id="show"></div>
        </body>
    </html>
    
    // 发布者处理发布消息事件
    var a = (function() {
        var count = 0
        var button = document.getElementById('count')
        button.onclick = function () {
            Event.trigger('add', count++)
        }
    })()
    
    // 发布者处理订阅事件
    var b = (function() {
        var div = document.getElementById('show')
        Event.listen('add', function(count) {
            div.innerHTML = count
        })
    })()
    

    五、离线储存+命名空间 - 解决离线事件

    • 需求实例:在聊天软件上,用户离线时收到朋友的消息,将这条消息保存下来,等到用户登录时,再把离线消息发给用户。即等到有对象来订阅它的时候,再重新把消息发布给订阅者。
    • 解决问题:
      • 离线消息
      • 先发布后订阅
      • 事件类型冲突

    重新定义 Event 对象

    • 仍然是立即执行函数
    • 返回一个包含不同方法的对象
    
    // Event 简略结构如下
    var Event = (() => {
    	var ... // 定义一些变量	
    	return (() => { // Event对象立即执行函数,返回一些函数
    		var ... // 一些变量
    		var _each = function(...){...} // 一些基本的工具函数,单一职责
    		var _create = function(...){...}
    		var _listen = function(...){...}
    		var _trigger = function(...){...}
    		var _remove = function(...){...}
    		// 在外部创建Event对象最终返回的对象
    		return { create: ..., one: ..., remove: ..., listen: ..., trigger: ...}
    	})()
    })()
    
    // Event的实现
    var Event = (() => {
        var _default = 'default' // 没有使用命名空间, 或者创建命名空间未传递名字时候, 使用的默认的命名空间
    
        return (() => {
            // _listen   监听函数 
            // _trigger  触发函数
            // _remove   移除函数 
            // _unshift  数组操作工具方法
            // _create   创建命名空间函数
            // _each     遍历工具函数
            // namespaceCache 命名空间 
            var _listen, _trigger, _remove, _unshift, _create, _each, namespaceCache
    
            namespaceCache = {}
            _unshift = Array.prototype.unshift
    
            /**
             * 对数组 arr 的每个元素进行操作 fn
             * 
             * @param {Array}    arr 要遍历的数组
             * @param {Function} fn  对于数组每一项进行操作的方法
             * @return 最后一个回调函数的执行结果 
             */
            _each = (arr, fn) => {
                var ret
                // 调用提供的函数, 作用域为elem, 参数为 元素elem下标、元素elem本身
                arr.forEach((elem, i) => ret = fn.call(elem, i, elem))
                // 返回最后一个回调函数的执行结果 
                return ret
            }
    
            /**
             * 添加订阅者fn
             * 没有订阅该事件则创建
             * 
             * @param {Object}   cache   给定命名空间下的缓存
             * @param {String}   eventId 事件名称标签
             * @param {Function} fn      代表订阅者,发布消息时以此fn来通知调用者
             */
            _listen = (cache, eventId, fn) => 
                cache[eventId] 
                    ? cache[eventId].push(fn) 
                    : cache[eventId] = [fn]
    
            /**
             * 发布消息
             * 
             * @param {Object} cache   给定命名空间下的缓存
             * @param {String} eventId 事件名称标签
             * @param {*}      message 发送的消息
             */
            _trigger = function (cache, eventId, message) {
                var _self = this
                var stack = cache[eventId]
    
                // 如果没有事件标签,或事件栈为空
                // 即没有此事件,或此事件没有订阅者,则直接返回
                if (!stack || stack.length === 0) {
                    return
                }
    
                // 遍历事件堆, 分别发布事件
                return _each(stack, function() {
                    return this.call(_self, message) // this 指向的是 stack[eventId] 中的函数
                })
            }
    
            /**
             * 取消订阅事件
             * 
             * @param {String}   eventId 事件名称
             * @param {Object}   cache   给定命名空间下的缓存
             * @param {Function} fn      代表订阅者,发布消息时以此fn来通知调用者
             */
            _remove = (eventId, cache, fn) => {
                // 如果有此类事件,则进行移除操作
                if (cache[eventId]) {
    
                    // 没有传递fn那么则定因为删除该事件中所有的订阅者 
                    if (!fn) {
                        cache[eventId] = []
                        return
                    }
    
                    // 删除cache中对应的订阅者
                    cache[eventId].forEach((_fn, i) => {
                        if (fn === _fn) cache[eventId].splice(i, 1)
                    })
                }
            }
    
            /**
             * 创建一个命名空间
             * 
             * @param {String} namespace 自定义的命名空间的名称
             */
            _create = (namespace) => {
                
                // 命名空间名称,没有提供则为 default
                var namespace = namespace || _default
    
                // 如果该命名空间已存在, 则直接返回
                if (namespaceCache[namespace]) {
                    return namespaceCache[namespace]
                }
    
                // 如果是新命名空间,则把此 namespace 新建到 Event 对象的 namespaceCache 中
                // 并初始化赋值
    
                // 此命名空间下的缓存
                // 形如 cache {
                //     "event1": [fn1, fn2, fn3, ...],
                //     "event2": [fn1, fn2, fn3, ...]
                // }
                var cache = {}
    
                // 此命名空间下的离线事件
                // 形如 offlineStack [fn1, fn2, fn3, ...]
                var offlineStack = []
    
                // 默认返回的对象
                // 包含 listen, one, remove, trigger 等函数
                return namespaceCache[namespace] = {
    
                    /**
                     * 添加订阅者到此命名空间下
                     * 
                     * @param {String}   event   事件标签,此命名空间下的事件标签
                     * @param {Function} fn      代表订阅者 
                     * @param {String}   last 
                     */
                    listen: (event, fn, last) => {
    
                        // 在此命名空间下的缓存 cache 里的时间标签 event 下,添加订阅者 fn 
                        _listen(cache, event, fn)
    
                        // 如果不存在离线事件,添加订阅者的函数完毕
                        if (!offlineStack) return
    
                        // 如果存在离线事件, 弹出并执行最后一条离线消息事件
                        if (last === 'last') { 
                            offlineStack.length && offlineStack.pop()()
                        } else { // 执行每条离线事件 每个离线消息都是一个function 
                            _each(offlineStack, function(){
                                this() // this 即为 offlineStack 中的函数 fn,this() 即为 fn()
                            })
                        }
    
                        // 清除离线消息缓存,添加订阅者的函数完毕
                        offlineStack = null
                    },
    
                    // 命名空间 监听一次函数 类似于jquery.one
                    one: function (eventId, fn, last) {
                        _remove(eventId, cache)
                        this.listen(eventId, fn, last); // 重新创建监听函数
                    },
    
                    remove: (eventId, fn) => _remove(eventId, cache, fn),
    
                    trigger: function () {
                        var fn, args, _self
    
                        _unshift.call(arguments, cache) // 把cache添加进arguments中
                        _self = this
                        args  = arguments
                        fn    = () => _trigger.apply(_self, args)
    
                        // 如果离线事件存在, 则直接执行
                        // 如果离线事件不存在, 则先把当前事件存到离线事件栈offlineStack里
                        return (offlineStack) ? offlineStack.push(fn) : fn()
                    }
                }
            }
    
            return {
                create: _create,
                one: function (eventId, fn, last) {
                    this.create().listen(eventId, fn, last)
                },
                remove: function (eventId, fn) {
                    this.create().remove(eventId, fn);
                },
                listen: function (eventId, fn, last) {
                    this.create().listen(eventId, fn, last);
                },
                trigger: function() { 
                    this.create().trigger.apply(this, arguments); 
                }
            }
        })()
    

    测试

    
    // 先发布后订阅 
    // 没有创建命名空间来监听和触发函数 实际上内部已经创建了一个命名空间为default
    // 1. 当先发布的时候实际上会添加进一个offlineStack这个堆里面
    Event.trigger("click", 1);
    
    // 2. 当监听的时候检测到有offlineStack则会先触发所有的离线事件
    // 而且监听一次后则会把所有的离线事件清空
    Event.listen("click", (a) => console.log(a)) // 1 
    
    // 使用命名空间 
    Event.create("namespace1").listen("click", (a) => console.log(a)) // 1
    Event.create("namespace1").trigger("click", 1) 
    Event.create("namespace2").listen("click", (a) => console.log(a)) // 2 
    Event.create("namespace2").trigger("click", 2)
    // console最终输出
    1
    1
    2
    
    展开全文
  • js发布订阅模式

    2020-03-31 20:30:23
    发布订阅模式广泛用于js中,从早期的jq事件池的机制到后面vue的eventbus都是典型的发布订阅模式。在该模式中,发布者向事件池中发送或移除事件,由事件池通知订阅者。这样大大降低了代码之间的耦合度,使得代码更...

    发布订阅模式广泛用于js中,从早期的jq事件池的机制到后面vue的eventbus都是典型的发布订阅模式。在该模式中,发布者向事件池中发送或移除事件,由事件池通知订阅者。这样大大降低了代码之间的耦合度,使得代码更利于维护。这一点也是和观察者模式最大的区别,观察者是发布者直接发送消息给订阅者,是强耦合的。

    一个简单的发布订阅模式

    let subscribe = function () {
      const isFunction = fn => typeof fn === 'function'
      class Sub {
        constructor() {
          this.eventArr = []
        }
        add(fn) {
          if (!isFunction(fn)) console.error('add must accept a function as first parameter')
          this.eventArr.includes(fn) ? null : this.eventArr.push(fn)
          return this
        }
        remove(fn) {
          if (!isFunction(fn)) console.error('remove must accept a function as first parameter')
          let index = this.eventArr.indexOf(fn)
          index > -1 ? this.eventArr.splice(index, 1) : null
          return this
        }
        fire(...args) {
          let self = this
          this.eventArr.map(function (item) {
            item.call(self, ...args)
          })
          return this
        }
      }
      return new Sub()
    }
    
    //测试
    let sub = subscribe()
    let fn1 = function () {
      console.log(1);
    }
    let fn2 = function () {
      console.log(2);
    }
    let fn3 = function (a,b) {
      console.log(a);
      console.log(b);
    }
    sub.add(fn1).add(fn2).add(fn3)
    sub.fire(3,4)           //1 2 3 4
    
    

    数组塌陷问题

    有点时候,我们在执行事件池中的函数时,可能会影响事件池。例如当我们执行以下代码时,发现它输出1 3,这是因为在执行fn1的时候,执行了remove方法,影响了当前事件池中的index,我们应期望当执行remove的时候只是将其置空,执行fire的时候再将其置空项删除

    let sub = subscribe()
    let fn1 = function () {
      console.log(1);
      sub.remove(fn1)
    }
    let fn2 = function () {
      console.log(2);
    }
    let fn3 = function () {
      console.log(3);
    }
    sub.add(fn1).add(fn2).add(fn3)
    sub.fire()             //1  3
    

    调整后代码如下

    let subscribe = function () {
      const isFunction = fn => typeof fn === 'function'
      class Sub {
        constructor() {
          this.eventArr = []
        }
        add(fn) {
          if (!isFunction(fn)) console.error('add must accept a function as first parameter')
          this.eventArr.includes(fn) ? null : this.eventArr.push(fn)
          return this
        }
        remove(fn) {
          if (!isFunction(fn)) console.error('remove must accept a function as first parameter')
          let index = this.eventArr.indexOf(fn)
          index > -1 ? this.eventArr[index] = null : null
          return this
        }
        fire(...args) {
          for (let index = 0; index < this.eventArr.length; index++) {
            if (!this.eventArr[index]) {
              this.eventArr.splice(index,1)
              index--
            } else {
              this.eventArr[index].call(this, ...args)
            }
          }
          return this
        }
      }
      return new Sub()
    }
    
    
    展开全文
  • JS 发布订阅模式

    2018-10-25 12:53:52
    发布订阅模式 事件发布/订阅模式 (PubSub) 在异步编程中帮助我们完成更松的解耦,甚至在 MVC、MVVC 的架构中以及设计模式中也少不了发布-订阅模式的参与。 优点:在异步编程中实现更深的解耦 缺点:如果过多的使用...
        

    首先声明,本文并非原创。原文请点击这里,本文是在原文的基础上加入一些自己的一些东西,方便以后自己理解与查看。

    发布订阅模式

    事件发布/订阅模式 (PubSub) 在异步编程中帮助我们完成更松的解耦,甚至在 MVC、MVVC 的架构中以及设计模式中也少不了发布-订阅模式的参与。

    优点:在异步编程中实现更深的解耦

    缺点:如果过多的使用发布订阅模式,会增加维护的难度

    实现发布订阅模式

    var Event = function() {
        this.obj = {}
    }
    
    Event.prototype.on = function(eventType,fn) {
        if(!this.obj[eventType]) {
            this.obj[eventType] = []
        }
        this.obj[eventType].push(fn)
    }
    
    Event.prototype.emit = function() {
        // 取第一个参数,作为eventType
        var eventType = Array.prototype.shift.call(arguments);
        //  获取事件数组
        var arr = this.obj[eventType];
        var len = arr.length;
        // 循环数组,一次执行其中的函数
        for(var i=0;i<len;i++) {
            // 直接调用arr[i],其this指向为undefined(严格模式下)
            // 因此用apply将this指向arr[i]
            // 数组shift函数取出第一个参数,将剩下的参数传入函数中
            arr[i].apply(arr[i],arguments)
        }
    }
    
    var ev = new Event()
    ev.on('click',function(a) {  // 订阅
        console.log(a)
    })
    
    ev.emit('click',1)   // 发布

    以上代码只能实现先订阅,再发布。直接发布就会报错。如何实现可以先发布,然后订阅?

    var Event = function() {
        this.obj = {};
        this.cacheList = [];
    }
    
    Event.prototype.emit = function() {
        const args = arguments;  //函数参数
        const that = this;  //this指向,保持cache函数的this指向
        function cache() {
            var eventType = Array.prototype.shift.call(arg)
            var arr = that.obj[eventType]
            for (let i = 0; i < arr.length; i++) {
              arr[i].apply(arr[i], arg)
            }
        }
        this.cacheList.push(cache)  // 采用闭包,保持对emit函数中参数和that的引用
    }
    
    Event.prototype.on = function(eventType,fn) {
        if(!this.obj[eventType]) {
            this.obj[eventType] = []
        }
        this.obj[eventType].push(fn)
        // 在订阅函数中执行emit函数中缓存的函数
        for (let i = 0; i < this.cacheList.length; i++) {
            this.cacheList[i]()
        }
    }
    

    改成这样后就实现了先发布函数,再订阅的过程。但是也只能先发布,然后再订阅,反过来就行不通。

    展开全文
  • js 发布订阅模式

    2017-09-09 17:39:00
    //发布订阅模式 class EventEmiter{ constructor(){ //维护一个对象 this._events={ } } on(eventName,callback){ if( this._events[eventName]){ //如果有就...
    //发布订阅模式
    class EventEmiter{
        constructor(){
            //维护一个对象
            this._events={
    
            }
        }
        on(eventName,callback){
            if( this._events[eventName]){
                //如果有就放一个新的
                this._events[eventName].push(callback);
            }else{
                //如果没有就创建一个数组
                this._events[eventName]=[callback]
            }
        }
        emit(eventName,...rest){
            if(this._events[eventName]){ //循环一次执行
                this._events[eventName].forEach((item)=>{
                    item.apply(this,rest)
                });
            }
        }
        removeListener(eventName,callback){
            if(this._events[eventName]){
                //当前数组和传递过来的callback相等则移除掉
                this._events[eventName]=
                    this._events[eventName].filter(item=>item!==callback);
            }
        }
        once(eventName,callback){
            function one(){
                //在one函数运行原来的函数,只有将one清空
                callback.apply(this,arguments);
                //先绑定 执行后再删除
                this.removeListener(eventName,one);
            }
            this.on(eventName,one);
                //此时emit触发会执行此函数,会给这个函数传递rest参数
        }
    }
    class Man extends EventEmiter{}
    let man=new Man()
    function findGirl() {
        console.log('找新的女朋友')
    }
    function saveMoney() {
        console.log('省钱')
    }
    man.once('失恋',findGirl);
    //man.on('失恋',findGirl) //失恋 ,绑定一个函数方法
    man.on('失恋',saveMoney)//失恋 ,绑定一个函数方法
    man.removeListener('失恋',saveMoney); //移除一个函数方法
    man.emit('失恋');
    //绑定一次,触发多次,也只执行一次。触发后一次将数组中的哪一项删除掉下次触发就不会执行

     

    转载于:https://www.cnblogs.com/null11/p/7498820.html

    展开全文
  • 下面小编就为大家带来一篇js 发布订阅模式的实例讲解。小编觉得挺不错的,现在就想给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 下面小编就为大家带来一篇node.js 发布订阅模式的实例。小编觉得挺不错的,现在就想给大家,也给大家做个参考。一起跟随小编过来看看吧
  • class Event { constructor () { this.handlers = {} } // on addEventListener(type,handler) { if(!(type in this.handlers)) { this.handlers[type] = [] } ... throw `请添加${type}事
  • 原生js观察者模式的的实现,主要用来管理回调函数队列,参考jquery的callbacks模块
  • node.js 发布订阅模式

    2017-09-09 17:46:00
    //导入内置模块 let EventEmitter = require('events'); let util=require('util'); //Man继承EventEmitter util.inherits(Man,EventEmitter); //创建一个函数 function Man(){} ...functio...
  • // 先来一个构造函数function Events() {this._events = {};// 定义一个事件池,保存...}/*** [on 订阅事件]* @param {String} eventName [事件名称]* @param {Function} fn [给事件添加的回调函数]* @return {null} ...
  • js设计模式 发布订阅模式发布订阅模式 发布订阅模式 发布订阅模式中有一个注册中心,有订阅和发布方法,订阅者需要带着方法去订阅一个事件,当这个事件被发布时,订阅者执行这个方法 import * as _ from 'lodash'; /...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,105
精华内容 442
关键字:

js发布订阅模式