如何注销.on(drawend,...)事件?事件重复触发排查与修复
问题分析与修复
问题描述
为#drawEvt注册drawend事件后,调用deattachAll()尝试注销事件失效,#evtListenerOnDrawEnd内的日志会多次输出。算法逻辑要求:必须先调用deattachAll()才能再次调用digitize(),首次调用digitize()后,未调用deattachAll()则不会再次执行digitize()。
代码片段
#evtListenerOnDrawEnd = (evt, mapBuilderInstance, rasterLayer) => { // ... 业务逻辑 } getDrawGeomForSource(src, geomType) { return new Draw({ source: src, type: geomType, }); } this.#drawEvt = this.#mapBuilderInstance.getDrawGeomForSource(this.#vectorSource, 'Polygon');// 初始化#drawEvt digitize() { this.#drawEvt.on('drawend', (evt) => this.#evtListenerOnDrawEnd( evt, this.#mapBuilderInstance, this.#rasterLayer )); } deattachAll() { this.#unregisterEvtListeners(); } #unregisterEvtListeners() { this.#mapBuilderInstance.unregisterEvtListener('drawend', this.#drawEvt, this.#evtListenerOnDrawEnd); } unregisterEvtListener(eventName, type, callbackListener) { if (type) { type.un(eventName, callbackListener); } }
原因分析
- 监听器绑定与注销不匹配:在
digitize()方法中,绑定的是匿名箭头函数,该函数内部调用#evtListenerOnDrawEnd,但注销时传入的是原函数#evtListenerOnDrawEnd——两者并非同一个函数引用,导致un()方法无法找到对应的监听器完成注销。 - 重复绑定累积:即使逻辑上限制了
digitize()的调用时机,但由于注销失效,每次合法调用digitize()都会新增一个匿名监听器,最终drawend事件触发时,多个监听器同时执行,造成日志多次输出。
修复方法
方案一:直接绑定原函数并通过bind传递参数
修改digitize()方法,直接绑定#evtListenerOnDrawEnd,用bind预设额外参数,确保绑定和注销的是同一个函数引用:
// 初始化时保存绑定后的函数引用 this.#boundDrawEndListener = this.#evtListenerOnDrawEnd.bind( this, null, // evt参数由事件触发自动传入,此处占位 this.#mapBuilderInstance, this.#rasterLayer ); digitize() { this.#drawEvt.on('drawend', this.#boundDrawEndListener); } #unregisterEvtListeners() { this.#mapBuilderInstance.unregisterEvtListener('drawend', this.#drawEvt, this.#boundDrawEndListener); }
注意:
bind会返回新的函数实例,需保存该实例用于后续注销,不可每次调用bind(否则每次生成新函数,依然无法匹配注销)。
方案二:保存匿名函数的引用
若需保留匿名函数写法,需将该匿名函数保存到实例变量中,注销时使用该引用:
// 类实例中新增变量保存监听器引用 #drawEndListener = null; digitize() { // 先注销已存在的监听器,防止重复绑定 if (this.#drawEndListener) { this.#drawEvt.un('drawend', this.#drawEndListener); } this.#drawEndListener = (evt) => this.#evtListenerOnDrawEnd( evt, this.#mapBuilderInstance, this.#rasterLayer ); this.#drawEvt.on('drawend', this.#drawEndListener); } #unregisterEvtListeners() { this.#mapBuilderInstance.unregisterEvtListener('drawend', this.#drawEvt, this.#drawEndListener); // 注销后清空引用,避免内存泄漏 this.#drawEndListener = null; }
额外优化
可在digitize()内部先检查是否已绑定监听器,或先注销再绑定,进一步避免意外的重复绑定,增强代码健壮性。
内容的提问来源于stack exchange,提问作者user22470297
相关产品推荐
相关产品推荐

