如何在jQuery中正确移除已绑定的事件处理程序
嘿,我来帮你捋捋这个问题——你已经知道用$.proxy绑定事件后可以通过引用移除,但绑定多个不同处理程序时所有之前的都出异常,这大概率是踩了$.proxy或者事件绑定的几个常见坑,我给你拆解下:
先看你贴的这段示例代码,本身是没问题的:
var func = function() { $('body').append('<div>' + this.a + '</div>'); }, a1 = $.proxy(func, { a: 1 }); $('body').on('aa', a1); $('body').off('aa', a1); $('body').triggerHandler('aa');
这段代码里你缓存了$.proxy生成的函数实例a1,绑定后用同一个引用移除,触发事件时确实不会有输出,因为处理程序已经被清掉了。但绑定多个处理程序时出异常,常见原因有这几个:
多次调用
$.proxy生成了不同的函数实例:哪怕你用同一个原函数和上下文,每次调用$.proxy都会返回一个全新的函数。如果绑定的时候用了新生成的proxy,移除的时候却拿另一个新的proxy去匹配,根本移除不掉,残留的处理程序就可能因为逻辑冲突引发异常。比如这种错误写法:// 绑定两次,但每次都是新的proxy实例 $('body').on('aa', $.proxy(func, {a:1})); $('body').on('aa', $.proxy(func, {a:2})); // 尝试移除第一个,但用的是新生成的proxy,完全匹配不上 $('body').off('aa', $.proxy(func, {a:1}));这时候移除无效,触发事件时两个处理程序都会跑,如果你的上下文或者函数逻辑有冲突,自然会出问题。
移除时不小心清掉了所有处理程序:如果你调用
$('body').off('aa')不带第二个参数,会直接移除该事件的所有处理程序。要是你本来只想删某一个,却误写了无参的off,那剩下的逻辑就会因为处理程序被意外移除而异常。处理程序内部的上下文或变量冲突:如果多个处理程序都依赖
this上下文,或者修改了同一个全局变量、DOM元素,也可能导致互相干扰,出现异常。
给你几个可行的解决办法:
缓存每个
$.proxy生成的函数实例
把每个proxy函数都存到变量里,绑定和移除都用同一个变量,确保引用完全一致:var func = function() { $('body').append('<div>' + this.a + '</div>'); }; // 缓存每个proxy实例 var handler1 = $.proxy(func, {a:1}); var handler2 = $.proxy(func, {a:2}); $('body').on('aa', handler1); $('body').on('aa', handler2); // 只移除handler1,handler2不受影响 $('body').off('aa', handler1); $('body').triggerHandler('aa'); // 只会执行handler2,输出<div>2</div>用事件命名空间精准管理
给每个处理程序加上专属的命名空间,这样不用管函数引用,也能精准移除某一类处理程序:var func = function() { $('body').append('<div>' + this.a + '</div>'); }; $('body').on('aa.handler1', $.proxy(func, {a:1})); $('body').on('aa.handler2', $.proxy(func, {a:2})); // 只移除带handler1命名空间的处理程序 $('body').off('aa.handler1');隔离处理程序的内部逻辑
如果异常是因为多个处理程序的逻辑互相干扰(比如共用变量),可以用立即执行函数(IIFE)给每个处理程序创建独立作用域,避免变量污染:// 给handler1创建独立作用域 (function(){ var localData = '来自handler1的数据'; $('body').on('aa', function(){ console.log(localData); // 这里的逻辑不会和其他处理程序冲突 }); })(); // 给handler2创建独立作用域 (function(){ var localData = '来自handler2的数据'; $('body').on('aa', function(){ console.log(localData); }); })();
你可以对照这些点排查下你的代码,应该就能找到问题所在啦~
内容的提问来源于stack exchange,提问作者Roland Soós

