IE11中addEventListener函数变量作用域问题求助
解决IE11中循环内事件回调无法访问外部变量的问题
这是个非常典型的闭包作用域陷阱,在旧版IE(比如IE11)里会直接暴露出来,Chrome等现代浏览器因为对作用域的处理有优化,可能暂时没出现问题,但本质上代码存在隐患。
问题原因
你的点击事件回调是在循环结束后才触发执行的,而回调里引用的i是循环中的同一个外层变量——当循环完成时,i的值已经超出了cipher数组的索引范围(比如循环到最后i等于数组长度,这时候cipher[i]自然不存在)。IE11对作用域的绑定规则更严格,所以直接出现了无法访问的问题。
修复方案
下面提供几个可以直接在IE11中运行的解决方案:
方案1:用立即执行函数(IIFE)创建独立作用域
通过IIFE把当前循环迭代的cipher[i]捕获到独立作用域中,确保每个回调绑定的都是当前循环的元素:
if (cipher[i].solved == false){ bottom.classList.add("HighlightBox"); bottom.classList.add("bg-warning"); // 用IIFE捕获当前的cipher元素 (function(currentCipher) { bottom.addEventListener('click', function(){ currentCipher.solved = true; hintsUsed++; runHint(true); populateAnswerKey(puzzleWord, cipher, false); populatePuzzleBoard(puzzleWord, cipher); }); })(cipher[i]); // 传入当前迭代的cipher[i] }
方案2:用bind绑定参数
利用Function.prototype.bind()把当前的cipher元素绑定到回调函数的参数中,这样触发点击时就能拿到正确的元素:
if (cipher[i].solved == false){ bottom.classList.add("HighlightBox"); bottom.classList.add("bg-warning"); // 定义独立的回调函数 function handleClick(currentCipher) { currentCipher.solved = true; hintsUsed++; runHint(true); populateAnswerKey(puzzleWord, cipher, false); populatePuzzleBoard(puzzleWord, cipher); } // 绑定当前的cipher[i]作为回调的第一个参数 bottom.addEventListener('click', handleClick.bind(null, cipher[i])); }
额外说明:ES6 let的方案(需转译)
如果你的项目有ES6转译工具(比如Babel),可以把循环变量i换成let,这样每次循环都会创建一个独立的i变量,每个回调引用的都是当前迭代的i:
// 循环改为用let声明i for (let i = 0; i < cipher.length; i++) { if (cipher[i].solved == false){ bottom.classList.add("HighlightBox"); bottom.classList.add("bg-warning"); bottom.addEventListener('click', function(){ cipher[i].solved = true; hintsUsed++; runHint(true); populateAnswerKey(puzzleWord, cipher, false); populatePuzzleBoard(puzzleWord, cipher); }); } }
注意:IE11原生不支持
let,所以这个方案必须转译为ES5代码才能在IE11中正常运行。
内容的提问来源于stack exchange,提问作者Dan White
相关产品推荐
相关产品推荐

