js中订阅者和观察者能否给一个实际开发中用例子?

网上到处都是订阅者和观察者的代码实现,现在不知道什么场景,或者更简单实际的例子说明一下问题?

阅读 2.9k
5 个回答

真实的例子——网站登录

假如我们正在开发一个商城网站,网站里有 header 头部、nav 导航、消息列表、购物车等模块。

如果它们和用户信息模块产生了强耦合,比如下面这样 的形式:

login.succ(function(data){
 header.setAvatar( data.avatar); // 设置 header 模块的头像
 nav.setAvatar( data.avatar ); // 设置导航模块的头像
 message.refresh(); // 刷新消息列表
 cart.refresh(); // 刷新购物车列表
}); 

等到有一天,项目中又新增了一个收货地址管理的模块,这个模块本来是另一个同事所写的,在最后部分加上这行代码:

 login.succ(function( data ){
 header.setAvatar( data.avatar);
 nav.setAvatar( data.avatar );
 message.refresh();
 cart.refresh();
 address.refresh(); // 增加这行代码
});

我们就会越来越疲于应付这些突如其来的业务要求,要么跳槽了事,要么必须来重构这些代码。
摘自个人博客:订阅者实际应用

比如 VueReact 等等框架中

视图层的输入能影响到模型层,反之亦然,即双向绑定

这里的 怎么影响,就是靠订阅者观察者,因为 js 没有 Java 之类语言有的反射能力,只能靠这个去做一些数据绑定的事情。

一般的用在2个业务相互独立的模块或流程中。
比如说 有个视频播放器控件 提供了播放/停止的回调 叫onPlay 每当视频开始播放或者暂停的时候就会调用该方法

// status: 播放状态 1play 0pause 
function onPlay(status){}

正常情况下我们要在回调里做什么处理只能先新建一个播放器,然后注册回调来执行相关逻辑

var player = new Player()
player.onPlay = function(status){
    console.log(status === 1 ? '播放' : '暂停')
}

但是如果说有个场景,是处于你这个模块的外层,感知不到player这个变量,不知道这个视频有没有注册。
比如说在页面右上角有一个小图标 自动切换播放/停止的状态 那怎么办?这个时候你用了观察者模式,只需要注册订阅该事件就完了,然后比如说其他还有什么地方需要知道视频的播放状态的,也是订阅一下就好了
正因为这样的模式,会导致代码的可读性变差,因为逻辑不是写在一个地方的,所以在使用的时候需要权衡利弊

简单来说 大部分 主流框架 都 这个影子, jquery 里的 promise实现 和 事件系统 貌似 也是 这种。
redux 、 vuex 状态管理也是这种, 好多好多 都有。
我感觉这种实现, 也是为程序 解耦提供 方便, 我感觉。
自己写的 简单订阅

 export  const Event = function () {
       // 类型
       this.typeList = {}
    }
    Event.prototype.on = function ({type, fn}){
        if (this.typeList.hasOwnProperty(type)) {
            this.typeList[type].push(fn)
        } else {
            this.typeList[type] = []
            this.typeList[type].push(fn)
        }
    }
    Event.prototype.off = function({type, fn})  {
       if (this.typeList.hasOwnProperty(type)) {
             let list = this.typeList[type]
          let index = list.indexOf(fn)
          if (index !== -1 ) {
                 list.splice(index, 1)
          }
          
       } else {
            console.warn('not has this type')
       }
    }
    Event.prototype.once = function ({type, fn}) {
       const fixFn = (value) => {
            fn.call(this, value)
            this.off({type, fn: fixFn})
       }
       this.on({type, fn: fixFn})
    }
    Event.prototype.trigger = function (type, value, context){
        context = context ? context : null    
        if (this.typeList.hasOwnProperty(type)) {
            this.typeList[type].forEach(fn => {
                fn.call(context, value)
            })
        }
    }

dom事件机制:先订阅事件,然后在触发事件的时候,执行回调

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题