JavaScript 发布-订阅模式实例详解

作者:青柠

时间:2026-10-31

浏览:275

本文详细解析 JavaScript 中的发布-订阅模式,涵盖核心概念、基础代码实现及针对特定事件过滤的改进方案。通过缓存数组管理订阅者,利用 key 区分不同事件类型,并实现取消订阅功能。最后结合网站登录场景,展示如何在实际项目中应用该模式解耦模块间通信。

文章目录

一、核心概述

发布-订阅模式的核心结构主要包含三个部分:用于存储回调函数的缓存数组、负责注册回调的订阅操作,以及触发回调执行的发布操作。

缓存数组一个数组[]
订阅往数组里面压入函数fn
发布里面循环遍历数组,然后执行数组中的函数。

二、简单代码实现及改进

(1)基础实现

  var subpub = {};
  subpub.cache = [];
  subpub.subscribe = function(fn){
       this.cache.push(fn)
  }
  subpub.publish = function(){
  for(var i in this.cache){
         this.cache[i].apply(this,arguments)
       }
  }
   //test
   subpub.subscribe(function(price,square){
         console.log("price:"+price)
         console.log(square)
   })
   subpub.publish(200,102);
   subpub.publish(300,153)

(2)改进方案:针对订阅者无需接收所有发布事件的需求,引入 key 参数进行事件分类。

 var subpub = {};
        subpub.cache = {}; //是一个数组对象
        subpub.subscribe = function (key, fn) {
            if (!this.cache[key]) { //订阅的事情在缓存里面没有,则添加到缓存
                this.cache[key] = [];
            }
            this.cache[key].push(fn);
        }
        subpub.publish = function () {
            //传入参数的第一个是指订阅的类型
            var key =  Array.prototype.splice.apply(arguments,[0,1]);
            for (var i in this.cache[key]) {
                this.cache[key][i].apply(this, arguments)
            }
        }
        //test
        subpub.subscribe('lowPrice',function (price, square) { //A订阅价格低于300的
            console.log("price:" + price)
            console.log(square)
        })
        subpub.subscribe('highPrice',function (price, square) { //B订阅价格高于300的
            console.log("price:" + price)
            console.log(square)
        })
        subpub.publish('lowPrice',200, 102);
        subpub.publish('highPrice',400, 153)

取消订阅的事件处理

subpub.subscribe('lowPrice', fn1 = function (price, square) { //A订阅价格低于300的
            console.log(square)
        })
        subpub.subscribe('lowPrice', fn11 = function (price, square) { //A订阅价格低于300的
            console.log("price1:" + price)
            console.log(square)
        })
        subpub.subscribe('highPrice', fn2 = function (price, square) { //B订阅价格高于300的
            console.log("price:" + price)
            console.log(square)
        })
        //取消订阅的事件
        subpub.remove = function (key, fn) {
            if (!this.cache[key]) { //如果key对应的消息没有被订阅,则直接返回
                return false
            }
            if(!fn){  //如果没有传入fn,则移除key对应的所有消息
                this.cache[key] && (this.cache[key].length=0)
            } else {
                this.cache[key] = this.cache[key].filter(function (cur, index) {
                    return cur !== fn
                })
            }
        }
        subpub.remove('lowPrice',fn1)
        subpub.publish('lowPrice', 200, 102);

在取消订阅时,需要区分不同的情况。上述实现中使用了 filter 函数来过滤并移除需要取消订阅的具体事件回调。

三、现实项目需求

以网站登录场景为例:用户登录后,需要在多个地方显示客户信息。为了避免在用户登录后依次调用各个模块的刷新或其他相关函数,应采用监听登录这一单一操作的方式,通过发布-订阅机制通知所有相关模块。

  //封装一下
        var event = {
            cache: {},
            subscribe: function (key, fn) {
                if (!this.cache[key]) { //订阅的事情在缓存里面没有,则添加到缓存
                    this.cache[key] = [];
                }
                this.cache[key].push(fn);
            },
            publish: function () {
                //传入参数的第一个是指订阅的类型
                var key = Array.prototype.splice.apply(arguments, [0, 1]);
                for (var i in this.cache[key]) {
                    this.cache[key][i].apply(this, arguments)
                }
            },
            remove: function (key, fn) {
                if (!this.cache[key]) { //如果key对应的消息没有被订阅,则直接返回
                    return false
                }
                if (!fn) { //如果没有传入fn,则移除key对应的所有消息
                    this.cache[key] && (this.cache[key].length = 0)
                } else {
                    this.cache[key] = this.cache[key].filter(function (cur, index) {
                        return cur !== fn
                    })
                }
            }
        };
        var eventForPub = function (obj) {
            for (var i in event) {
                obj[i] = event[i]
            }
        }
        var login = {};
        eventForPub(login)
        var headItem = (function () {
            login.subscribe('login', function (data) {
                headItem.setAvatar(data.avatar)
            })
            return {
                setAvatar: function (data) {
                    console.log("头部显示客户信息")
                }
            }
        })();
        var buyItem = (function () {
            login.subscribe('login', function (data) {
                buyItem.setAvatar(data.avatar)
            })
            return {
                setAvatar: function (data) {
                    console.log("购物车显示客户信息")
                }
            }
        })();
        var data={avatar:'qqqq'}
        login.publish('login',data);

运行结果如下:

一、核心概述

更多设计模式相关知识点,还可以参考本站文章:

https://www.jb51.net/article/252965.htm

https://www.jb51.net/article/27973.htm

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章