Javascript设计模式之"观察者模式"

  • 编辑时间: 2018-10-19 20:25:36
  • 浏览量: loading...
  • 作者: 段亮

    前言:最近在看vuejs相关源码的时候发现,有个词语反复的出现在自己的脑海里。那么是哪个神奇的词语呢?就是大家经常听到的“观察者模式" ,对于前端而言用(发布-订阅)模式,来描述它更加亲切。


一、什么是观察者模式?

    观察者模式:它定义对象间一种一对多的依赖关系,当一个对象的状态发生改变的时候,所有依赖他的对象都将得到通知。可能这样说,对于前端小伙伴们,还是一个比较模糊的概念。那我们列举一个例子。比如:

DOM事件的绑定,就是一个经典的发布-订阅模式

document.body.addEventListener('click', function () {
    alert(2)
}, false)

document.body.click() // 模拟用户的点击


在这里我们需要监听用户点击document.body的动作,但是我们无法知道用户什么时候点击,所以我们订阅document.body上的click事件,当body节点被点击时,body节点会向订阅者发布这个消息。

其实vuejs的双向数据绑定:也是利用观察者模式 + Object.defineProperty()对数据进行劫持来实现双向数据绑定的。后续有时间,可以写一篇文章来分析下!


二、观察者模式的好处是什么?

    在我们了解到什么是观察者模式后,那么它在编程中有哪些好处呢?

        1、用于异步编程回调,代替传统的回调函数的方案

        2、可以取代对象之间硬编码的通知机制,代码解耦。 (如果有用vuejs做开的朋友可能会发现,Event Bus就是利用观察者模式来实现的)
// 比如,用户登录,通知其他模块更新
// 传统做法
login.success(function(data) {
    // 更新用户状态
    common.updateUserInfo()
    
    //刷新购物车
    cart.update()
    
    ...
})

// 采用观察者模式
$.ajax('http://xxx.com/login', function (data) {
    // 登录成功后,触发订阅事件
    subsEvent.tigger('loginSuccess', data)
})

// 各模块监听

var header = function () {
    subsEvent.listen('loginSuccess', function (data) {
        console.log('登录成功回调', data)
    })
}

var nav = function () {
    subsEvent.listen('loginSuccess', function (data) {
        console.log('登录成功回调', data)
    })
}


三、如何实现一个观察者模式呢?

    聊了这么多,那么我们自己动手实现一个简单的观察者模式吧!

    var subsEvent = (function () {
            var clientList= [], // 缓存订阅者回调函数
                listen,
                tigger,
                remove;

            listen =  function (key, fn) {
                if (!clientList[key]) {
                    clientList[key] = []
                }
                clientList[key].push(fn) // 订阅消息添加到消息缓存列表
            }
              
            tigger = function () {
                var key = Array.prototype.shift.call(arguments), // 取出消息类型
                    fns = clientList[key] // 取出回调函数
                if (!fns || fns.length === 0) {
                    return false
                }
                for (var i =0; i < fns.length; i++) {
                    var fn = fns[i]
                    fn.apply(this, arguments)
                }
            }
            
            remove = function (key, fn) { // 删除订阅消息
                var fns = clientList[key]
                
                if (!fns) {
                    return false
                }
                
                if (!fn) { // 删除key所有的订阅
                    fns = []
                } else {
                    for (var i = 0; i < fns.length; i++) { // 删除对应的订阅事件
                        var _fn = fns[i]
                        if (_fn === fn) {
                            fns.splice(i, 1)
                        }
                    }
                }
                
            }
            return {
                listen: listen,
                tigger: tigger,
                remove: remove
            }
    })()
        
    // 订阅消息 
    subsEvent.listen('test1', fn1 = function (data) {
        console.log('触发 test1:', data)
    })
        
    subsEvent.listen('test2', fn2 = function (data) {
         console.log('触发 test2:', data)
    })
    
    // 触发订阅消息
	subsEvent.tigger('test1', '大家好,我是段亮,这是我写的一个观察者模式!')

        
    // 模拟异步场景调用
    setTimeout(() => {
       subsEvent.tigger('test2', '没关系, 继续加油!🆙')

    }, 1000)
        
	// 删除订阅事件
   	subsEvent.remove('test2', fn2)


    写在最后:最后留个探讨的问题:观察者模式,一定要先订阅,才能发布吗?如果某些业务场景下,需要先发布,后订阅呢?又该如何实现呢?哈哈哈哈,欢迎一起讨论交流!😀

    很多时候,我们往往只是停留在对技术的使用,对于技术的底层实现和拓展往往忽略!

    这让我想起大家经常在调侃,阿里面试中经常会遇到的一个套路:“xxx技术你有了解吗?那你知道有啥问题吗?如果是你,你会怎么解决它。解决方案是什么?以及要注意些什么?xxxx”。

    

    本文由段亮博客,原创出品,如需转载请注明出处。
    本文出处:https://www.duanliang920.com/learn/web/7435.html


写文章不易,如果您觉得文章对你有帮助。
打赏激励下作者吧,谢谢支持! ~(@^_^@)~!

微信打赏 微信打赏

支付宝打赏 支付宝打赏

表情
0 条评论 最热评论 最新评论
  • 亲,暂时没有评论哦!赶紧来留下你的脚印吧!

你也想建立一个独立博客?

你是否也想打造一个在互联网上的个人品牌,成为一个家喻户晓的人物呢?
请点击了解 怎样创建个人博客

最新推荐
loading... 数据加载中...

超低服务器价格,值得购买!