You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义JavaScript对象事件派发重复问题排查

问题原因

你遇到的事件重复触发问题,核心原因是所有PlaylistManager实例共享了同一个事件存储对象:

  • 你通过PlaylistManager.prototype = new EventDispatcher()的方式继承EventDispatcher,这会创建一个EventDispatcher实例并挂载到PlaylistManager的原型上。
  • 由于原型链的特性,当PlaylistManager的实例自身没有events、addEventListener等属性时,会直接使用原型上的对应属性。
  • 这就导致所有PlaylistManager实例的事件处理函数都被添加到同一个events对象里,触发任意一个实例的事件时,所有实例绑定的处理函数都会被执行。
修复方案

要让每个PlaylistManager实例拥有独立的事件存储,需要在子类构造函数中调用父类的构造函数,为每个实例初始化自己的events和事件方法:

(function() {
  var EventDispatcher = function() {
    this.events = {};
  };

  EventDispatcher.prototype.addEventListener = function(name, handler) {
    if (this.events.hasOwnProperty(name)) {
      this.events[name].push(handler);
    } else {
      this.events[name] = [handler];
    }
  };

  EventDispatcher.prototype.removeEventListener = function(name, handler) {
    if (!this.events.hasOwnProperty(name)) return;

    var index = this.events[name].indexOf(handler);
    if (index != -1) this.events[name].splice(index, 1);
  };

  EventDispatcher.prototype.fireEvent = function(name, args) {
    if (!this.events.hasOwnProperty(name)) return;

    if (!args || !args.length) args = [];

    var evs = this.events[name],
      l = evs.length;
    for (var i = 0; i < l; i++) {
      evs[i].apply(null, args);
    }
  };

  window.EventDispatcher = EventDispatcher;
}(window));


(function(window) {
  "use strict"
  var PlaylistManager = function(data) {
    // 调用父类构造函数,为当前实例初始化events
    EventDispatcher.call(this);

    this.test = function() {
      this.fireEvent('PlaylistManager.COUNTER_READY', [{
        counter: "a"
      }]);
    }
  }

  // 用Object.create设置原型,避免直接实例化父类导致的共享问题
  PlaylistManager.prototype = Object.create(EventDispatcher.prototype);
  // 修正构造函数指向,保证原型链正确
  PlaylistManager.prototype.constructor = PlaylistManager;

  window.PlaylistManager = PlaylistManager;

}(window));


var _PlaylistManager = new PlaylistManager();
_PlaylistManager.addEventListener('PlaylistManager.COUNTER_READY', function(e) {
  console.log('COUNTER_READY')
})

var _PlaylistManager2 = new PlaylistManager();
_PlaylistManager2.addEventListener('PlaylistManager.COUNTER_READY', function(e) {
  console.log('COUNTER_READY 2')
})

_PlaylistManager.test() // 仅输出 COUNTER_READY
_PlaylistManager2.test() // 仅输出 COUNTER_READY 2

关键修改点:

  1. 将EventDispatcher的方法移到它的原型上,实现方法复用,同时每个实例通过调用构造函数拥有独立的events对象。
  2. 在PlaylistManager构造函数中调用EventDispatcher.call(this),确保每个实例初始化自己的events。
  3. 使用Object.create(EventDispatcher.prototype)设置子类原型,避免直接实例化父类带来的共享属性问题,同时修正constructor指向保证原型链完整性。

内容的提问来源于stack exchange,提问作者Toniq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 04:50:24