知其然
首先先來看下面一個(gè)例子(文字部分為源碼,亦有方便讀者查看)
<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>
點(diǎn)擊fun1 add按鈕,添加按鈕事件后,點(diǎn)擊aim按鈕可以成功顯示triggered!success.然而點(diǎn)擊fun1 remove按鈕嘗試移除事件后,再次點(diǎn)擊aim按鈕,卻仍然顯示出triggered!success.
可見,雖然是同名函數(shù),并且綁定了相同的作用域,然而移除方法并沒有生效.這是為什么呢?
知其所以然
在<js高級(jí)程序設(shè)計(jì)>一書中,是這么描述bind方法的:
ECMAScript 5 還定義了一個(gè)方法: bind().這個(gè)方法會(huì)創(chuàng)建一個(gè)函數(shù)的示例,其this值會(huì)被綁定到傳給bind()函數(shù)的值.(由于時(shí)間倉促找不到對(duì)應(yīng)在線文獻(xiàn),以上字符均為手打,心疼筆者的請(qǐng)點(diǎn)個(gè)贊)
這里的描述有個(gè)關(guān)鍵字:創(chuàng)建.與call/apply不同,bind屬性函數(shù)并不是針對(duì)函數(shù)對(duì)象的使用,而是創(chuàng)建一個(gè)新的函數(shù)對(duì)象.
眾所周知,函數(shù)名其實(shí)是函數(shù)對(duì)象的指針,func.bind(obj)這個(gè)語句中,func是指向的是同一個(gè)函數(shù)對(duì)象,而整個(gè)語句確實(shí)一個(gè)創(chuàng)建型的函數(shù),bind()方法執(zhí)行后,將會(huì)創(chuàng)建一個(gè)新的函數(shù)對(duì)象,并綁定對(duì)應(yīng)的作用域,在增加事件跟移除事件的時(shí)候雖然調(diào)用的是相同語句,然而兩者返回的卻是不同的函數(shù)引用,自然不能正確的移除dom的click事件.
原因找到了.解決之前問題的辦法自然而然就浮現(xiàn)出來了.使用func.bind(obj)的時(shí)候可以將返回的函數(shù)引用在外部存儲(chǔ)起來,在移除事件的時(shí)候使用即可,改良后的代碼如下:
改良后移除事件后再點(diǎn)擊aim按鈕便不會(huì)顯示triggered!success.了
本文中的所有代碼與實(shí)際的頁面展示均可以在筆者codepen上看到與體驗(yàn):https://codepen.io/pandaboxer/pen/GRozxyJ
更新不易,點(diǎn)個(gè)贊再走吧靚仔