自定义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
关键修改点:
- 将
EventDispatcher的方法移到它的原型上,实现方法复用,同时每个实例通过调用构造函数拥有独立的events对象。 - 在
PlaylistManager构造函数中调用EventDispatcher.call(this),确保每个实例初始化自己的events。 - 使用
Object.create(EventDispatcher.prototype)设置子类原型,避免直接实例化父类带来的共享属性问题,同时修正constructor指向保证原型链完整性。
内容的提问来源于stack exchange,提问作者Toniq
相关产品推荐
相关产品推荐

