问题1:闭包
考虑下面的代码:
var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', function() {
console.log('You clicked element #' + i);
});
}
请问,如果用户点击第一个和第四个按钮,控制台上会输出什么?为什么?
答案
上面代码的目的在于检测JavaScript的一个重要概念:闭包。对于每一个JavaScript开发者来说,如果你想在网页中编写5行以上的代码,那么准确理解和恰当使用闭包是非常重要的。如果你想开始学习或者只是想简单地温习一下闭包,那么我强烈建议你去阅读这个教程:Colin Ihrig 写的JavaScript Closures Demystified 。
好了,回到上面的代码。控制台会输出两次You clicked element #NODES_LENGTH,其中#NODES_LENGTH等于nodes的结点个数。由于闭包中变量的值不是静态的,i的值并不是添加click事件处理器时的值(比如,当给第一个button添加click事件处理器时i为0,给第二个添加时i为1)。当for循环结束时,变量i的值等于nodes的长度。因此事件被执行时,控制台会输出变量i当前的值,即等于nodes的长度。
问题2:闭包
修复上题的问题,使得点击第一个按钮时输出0,点击第二个按钮时输出1。
答案
有多种办法解决这个问题,下面我给出其中的两种。
第一个解决方案要用到一个IIFE来创建另外一个闭包,从而得到所希望的i的值。相应的代码如下:
var nodes = document.getElementsByTagName('button');
for (var i = 0; i < nodes.length; i++) {
nodes[i].addEventListener('click', (function(i) {
return function() {
console.log('You clicked element #' + i);
}
})(i));
}
另一个解决方案不使用IIFE,而是将函数移到循环的外面,代码如下:
function handlerWrapper(i) {
return function() {
console.log('You clicked element #' + i);
}
}
var nodes = document.getElementsByTagName('button');
for (var i = 0; i <nodes.length; i++) {
nodes[i].addEventListener('click', handlerWrapper(i));
}
来源:http://web.jobbole.com/81785/