removeEventListener 失效是因为 bind 每次返回新函数实例,而 removeEventListener 严格比对函数引用;必须用同一绑定后的函数实例添加和移除。

在控制层(比如事件处理函数所在的类或模块)中,用 bind 显式绑定 this 后,removeEventListener 会失效——根本原因不是“没删掉”,而是你传给 removeEventListener 的函数,和之前传给 addEventListener 的函数,**根本不是同一个引用**。
为什么 bind 后 removeEventListener 失效?
Function.prototype.bind() 每次调用都会返回一个**全新的函数实例**。即使参数完全一样,两次 bind 的结果也互不相等:
例如:
const handler = function() { console.log(this.id); };<br>const bound1 = handler.bind({ id: 'a' });<br>const bound2 = handler.bind({ id: 'a' });<br>console.log(bound1 === bound2); // false ← 关键!
而 removeEventListener 的机制是**严格比对函数引用**。如果你添加时用的是 handler.bind(obj),但移除时用了另一个 handler.bind(obj)(哪怕对象一模一样),浏览器就找不到匹配项,静默失败。
常见错误写法(踩坑现场)
以下代码看似合理,实则永远删不掉监听器:
- 在
addEventListener里直接写handler.bind(this),又在removeEventListener里重新写一遍handler.bind(this) - 在生命周期钩子(如 Vue 的
mounted、React 的useEffect)里反复bind并添加,却没保存绑定后的函数引用 - 把
bind写在箭头函数内部或每次渲染都执行的位置,导致每次都是新函数
正确解法:保存绑定后的函数引用
核心原则:**同一个函数实例,既用于添加,也用于移除**。推荐做法:
- 在类字段或闭包变量中提前绑定并缓存:
this.boundHandler = this.handler.bind(this);,之后统一用这个变量添加和移除 - 如果需动态绑定不同上下文,用 Map 或 WeakMap 缓存:
const cache = new WeakMap(); cache.set(element, handler.bind(context)); - 改用箭头函数(隐式绑定 this):在类中定义
handler = () => { ... },它天然保持 this 稳定,无需 bind,可直接增删 - 利用事件选项
{ once: true }:适合只执行一次的场景,避免手动移除
调试小技巧
怀疑没删掉?加个简单验证:
element.addEventListener('click', fn);<br>console.log('added:', fn);<br>// …<br>element.removeEventListener('click', fn);<br>console.log('removed:', fn); // 确保两个 fn 是同一个对象
也可以在开发者工具的 “Event Listeners” 面板里,展开对应元素,看监听器列表是否还存在——这是最直观的验证方式。

















