You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何注销.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);
    }
}

原因分析

  1. 监听器绑定与注销不匹配:在digitize()方法中,绑定的是匿名箭头函数,该函数内部调用#evtListenerOnDrawEnd,但注销时传入的是原函数#evtListenerOnDrawEnd——两者并非同一个函数引用,导致un()方法无法找到对应的监听器完成注销。
  2. 重复绑定累积:即使逻辑上限制了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 16:25:07