Class中用bind(this)绑定事件监听器,切换月份时如何避免重复绑定致崩溃?
问题描述
我有一个对象实例,其某一方法会为月份日期列表添加事件监听器:
addListenerToDays() { monthDays = this.instanceElement.querySelectorAll('.month-days'); for (let i = 0; i < monthDays.length; i++) { monthDays[i].addEventListener ("click", this.setSelection.bind(this), false); } }
问题在于切换月份时,每次都会为每个日期绑定约30个新的点击事件监听器,导致应用迅速变慢直至浏览器崩溃。我该如何正确绑定监听器并移除旧监听器?上述方法会在每次月份变化时被调用,这意味着再次调用时不再需要之前的监听器,但我该如何移除该方法之前添加的特定监听器?
解决方案
方法一:提前绑定回调函数,解决bind生成新函数的问题
bind()每次调用都会返回全新的函数,这就是你无法直接移除旧监听器的核心原因——你拿不到之前绑定的函数引用。可以提前把绑定好this的回调函数存起来:
- 在类的构造函数中预先绑定:
constructor() { // 其他初始化逻辑 this.boundSetSelection = this.setSelection.bind(this); }
- 修改
addListenerToDays方法,先移除旧监听器再添加新的:
addListenerToDays() { const monthDays = this.instanceElement.querySelectorAll('.month-days'); for (let i = 0; i < monthDays.length; i++) { // 先移除已存在的监听器 monthDays[i].removeEventListener("click", this.boundSetSelection, false); // 再绑定同一个预定义的回调函数 monthDays[i].addEventListener("click", this.boundSetSelection, false); } }
这样每次切换月份时,会先清理旧的监听器,再绑定同一个函数,不会造成监听器堆积。
方法二:事件委托(更优方案)
给每个日期单独绑定监听器效率低且容易堆积,不如把监听器绑定到日期元素的父容器上,利用事件冒泡处理点击,只需要一个监听器就能覆盖所有日期:
- 在初始化时给父容器绑定一次监听器(假设父容器类名为
month-container):
constructor() { // 其他初始化逻辑 const container = this.instanceElement.querySelector('.month-container'); container.addEventListener("click", this.handleDayClick.bind(this), false); }
- 编写事件处理函数,判断点击目标:
handleDayClick(e) { // 确认点击的是日期元素 if (e.target.matches('.month-days')) { this.setSelection(e); } }
这种方案不需要在切换月份时重复操作监听器,性能更好,代码也更简洁。
补充说明
如果切换月份时日期元素会被完全替换(比如通过innerHTML重新渲染),旧元素的监听器会随元素销毁被自动回收,但如果元素是被复用的,还是需要用上述方法处理。
内容的提问来源于stack exchange,提问作者pileup
相关产品推荐
相关产品推荐

