原生JS添加/移除事件监听器时如何保留类实例的this上下文?
浏览器原生事件跨组件通信this指向问题解决方案
核心原理:addEventListener和removeEventListener要求传入完全相同的函数引用才能匹配移除,同时事件回调默认this指向绑定事件的对象(此处为window),只要提前生成绑定好实例this的固定函数引用即可同时满足两个需求。
方案1:类箭头函数属性(现代JS最常用推荐)
直接将事件处理函数定义为类的箭头函数属性,箭头函数的this继承自定义时的外层作用域(即类实例),且函数引用固定,可正常添加移除。
class B { constructor() { this.someValue = '测试参数' } // 箭头函数类属性,this自动绑定到实例 doneHandler = () => { this.someMethod(this.someValue); // 可正常调用实例方法、访问实例属性 } someMethod(val) { console.log('执行成功', val) } setOption(shouldListen) { if (shouldListen) { window.addEventListener('done', this.doneHandler); } else { window.removeEventListener('done', this.doneHandler); } } }
适用场景:所有支持类字段语法的现代浏览器、使用编译工具的前端项目。
方案2:构造函数内预bind绑定
如果需要兼容不支持类字段语法的旧环境,可以在构造函数内提前把处理函数绑定实例后存为实例属性,后续添加移除都使用这个绑定后的固定引用。
class B { constructor() { this.someValue = '测试参数' // 提前绑定实例this,保存固定的函数引用 this.boundDoneHandler = this.doneHandler.bind(this) } doneHandler() { this.someMethod(this.someValue); // 可正常工作 } someMethod(val) { console.log('执行成功', val) } setOption(shouldListen) { if (shouldListen) { window.addEventListener('done', this.boundDoneHandler); } else { window.removeEventListener('done', this.boundDoneHandler); } } }
适用场景:需要兼容IE等老旧浏览器的项目。
方案3:使用DOM标准handleEvent接口
这是DOM事件规范内置的能力,直接把类实例本身传给addEventListener,只要类实现了handleEvent方法,事件触发时会自动调用该方法,且this默认指向类实例,无需额外绑定。
class B { constructor() { this.someValue = '测试参数' } // 实现标准handleEvent接口 handleEvent(e) { // 可以根据事件类型分支处理多个事件 if(e.type === 'done') { this.doneHandler(e) } } doneHandler() { this.someMethod(this.someValue); // 可正常工作 } someMethod(val) { console.log('执行成功', val) } setOption(shouldListen) { if (shouldListen) { // 直接传入实例本身,无需传入函数 window.addEventListener('done', this); } else { window.removeEventListener('done', this); } } }
适用场景:同一个实例需要监听多个不同事件的场景,代码结构更清晰。
内容的提问来源于stack exchange,提问作者Cirrocumulus
相关产品推荐
相关产品推荐

