知其然
首先先来看下面一个例子(文字部分为源码,亦有方便读者查看)
<body> <div> <span>result:</span> <span id="result"></span> </div> <button id="aim">aim btn</button> <button onclick="addFunc1()">fun1 add</button> <button onclick="removeFunc1()">fun1 remove</button></body><script> const aim = document.getElementById('aim'); const result = document.getElementById('result'); const obj = { content: 'success' }; const func = function() { result.innerText = 'triggered!' + this.content; }; const removeFunc1 = function() { result.innerText = 'fun1 removed'; aim.removeEventListener('click', func.bind(obj)) }; const addFunc1 = function() { result.innerText = 'fun1 added'; aim.addEventListener('click', func.bind(obj)); };</script>
点击fun1 add按钮,添加按钮事件后,点击aim按钮可以成功显示triggered!success.然而点击fun1 remove按钮尝试移除事件后,再次点击aim按钮,却仍然显示出triggered!success.
可见,虽然是同名函数,并且绑定了相同的作用域,然而移除方法并没有生效.这是为什么呢?
知其所以然
在<js高级程序设计>一书中,是这么描述bind方法的:
ECMAScript 5 还定义了一个方法: bind().这个方法会创建一个函数的示例,其this值会被绑定到传给bind()函数的值.(由于时间仓促找不到对应在线文献,以上字符均为手打,心疼笔者的请点个赞)
这里的描述有个关键字:创建.与call/apply不同,bind属性函数并不是针对函数对象的使用,而是创建一个新的函数对象.
众所周知,函数名其实是函数对象的指针,func.bind(obj)这个语句中,func是指向的是同一个函数对象,而整个语句确实一个创建型的函数,bind()方法执行后,将会创建一个新的函数对象,并绑定对应的作用域,在增加事件跟移除事件的时候虽然调用的是相同语句,然而两者返回的却是不同的函数引用,自然不能正确的移除dom的click事件.
原因找到了.解决之前问题的办法自然而然就浮现出来了.使用func.bind(obj)的时候可以将返回的函数引用在外部存储起来,在移除事件的时候使用即可,改良后的代码如下:
改良后移除事件后再点击aim按钮便不会显示triggered!success.了
本文中的所有代码与实际的页面展示均可以在笔者codepen上看到与体验:https://codepen.io/pandaboxer/pen/GRozxyJ
更新不易,点个赞再走吧靓仔