zoukankan      html  css  js  c++  java
  • JavaScript设计模式(三):发布—订阅模式(观察者模式)

    概述

    发布—订阅模式又叫做观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象将得到通知。在JavaScript中,我们一般用事件模型来替代传统的发布—订阅模式。

    • 发布者状态变化自动通知订阅者
    • 发布者对象与订阅者对象松耦合地联系在一起

    DOM事件

    我们在 DOM 节点上绑定的事件函数,就是最典型的发布—订阅模式。

    var event = new Event('alert')
    document.body.addEventListener('alert', function(){
        alert('oops')
    })
    document.body.dispatchEvent(event) //模拟事件
    

    我们无法预知用户何时会进行点击,因此我们订阅 body 上的 click 事件,当节点被点击时,body 节点便会向订阅者发布这个消息。

    document.body.addEventListener('alert', function(){
        alert('emmm')
    })
    

    还可以添加多个订阅者,订阅者与发布者松耦合。

    模式实现

    JavaScript 中通过回调函数来实现发布—订阅模式。

    • 确定发布者
    • 存放订阅者回调函数的缓存列表
    • 方法:订阅 / 退订 / 发布
    var publisher = {} // 定义发布者
    publisher.subList = {} // 指定存放回调的缓存列表
    publisher.listen = function(){
        //订阅
    }
    publisher.trigger = function(){
        //发布
    }
    publisher.remove = function(){
        //退订
    }
    

    也可是以下形式:

    var publisher = {
        subList: {},
        listen: function(){},
        trigger: function(){},
        remove: function(){}
    }
    

    订阅

    publisher.listen = function(type, fn){ // type:订阅类型 fn:订阅回调
        if(!this.subList[type]){ // 第一次订阅此类型时创建新缓存列表
            this.subList[type] = []
        }
        this.subList[type].push(fn) // 将订阅的回调添加至缓存列表
    }
    

    发布

    publisher.trigger = function(){
        var type = [].shift.call(arguments), // 取得消息类型
            fns = this.subList[type] // 取得对应消息类型的回调 
        if( !fns || fns.length == 0 ){ // 无此类型或无回调直接返回
            return false
        }
        for(var i = 0; i < fns.length; i++){
            fns[i].apply(this, arguments) // 遍历回调并调用
        }
        return this // 链式调用
    }
    

    退订

    publisher.remove = function(type, fn){ // 仅指定type时移除此类型的所有订阅
                                           // 同时指定fn时删除对应单个回调
        var fns = this.subList[type]
        if(!fns) return false // 如果无指定类型或无回调直接返回
        if(!fn){ // fn未指定
            fns && (fns.length = 0)
        }else{ // fn指定
            for(i = fns.length - 1; i >= 0; i--){ //反向遍历
                if(fn === fns[i]){
                    fns.splice(i, 1) //将指定对调删除
                }
            }
        }
    }
    

    使用

    publisher.listen('fruit', function(s){ // 订阅
        console.log('fruit: ' + s)
    })
    publisher.listen('fruit', fn1 = function(s){
        console.log('fruit#: ' + s)
    })
    
    publisher.trigger('fruit', 'banana') // 发布
    publisher.remove('fruit', fn1) // 退订
    publisher.trigger('fruit', 'apple')
    
    "fruit: banana"
    "fruit#: banana"
    "fruit: apple"
    

    原型模式实现

    function Publisher(){
        this.subList = {}
    }
    Publisher.prototype = {
        listen: function(){},
        trigger: function(){},
        remove: function(){}
    }
    
    var p = new Publisher()
    p.listen()
    

    先发布后订阅

    使用 setTimeout 将发布部分代码异步化,简单实现先发布后订阅的功能。

    publisher.trigger = function(){
        var type = Array.prototype.shift.call(arguments),
            _this = this,
            args = arguments
    
        setTimeout(function(){
            var fns = _this.cache[type]
            if(!fns || fns.length==0)
                return false
            for(var i = 0; i < fns.length; i++){
                fns[i].apply(_this, args)
            }
        }, 0);
      return this
    }
    

    JS Bin - 发布订阅模式

  • 相关阅读:
    PureBasic 打开一个一行有多个数据的文件并读取其中某个数据
    正则表达式的30分钟教程
    【编程珠玑】读书笔记 第一章 开篇
    while ((ch = getchar()) != EOF)中ch定义为char还是int型?cin、scanf等如何结束键盘输入
    itoa函数的实现(不同进制)
    atoi函数的实现(考虑不同进制、溢出)
    函数重载二义性:error C2668: 'pow' : ambiguous call to overloaded function
    strcat与strncat的C/C++实现
    strcpy函数的C/C++实现
    strlen的C/C+++实现
  • 原文地址:https://www.cnblogs.com/qimeng/p/7989501.html
Copyright © 2011-2022 走看看