文章出自http://www.cnblogs.com/snandy/archive/2011/03/01/1967628.html
有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<!DOCTYPE HTML>
<html>
<head>
<meta charset=
"utf-8"
/>
<title>闭包演示</title>
<style type=
"text/css"
>
p {background:gold;}
</style>
<script type=
"text/javascript"
>
function
init() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
pAry[i].onclick =
function
() {
alert(i);
}
}
}
</script>
</head>
<body onload=
"init();"
>
<p>产品 0</p>
<p>产品 1</p>
<p>产品 2</p>
<p>产品 3</p>
<p>产品 4</p>
</body>
</html>
|
以上场景是初学者经常碰到的。即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。
原因是初学者并未理解JavaScript的闭包特性。通过element.οnclick=function(){alert(i);}方式给元素添加点击事件。响应函数function(){alert(i);}中的 i 并非每次循环时对应的 i(如0,1,2,3,4)而是循环后最后 i 的值5。 或者说循环时响应函数内并未能保存对应的值 i,而是最后一次i++的值5。
了解了原因,摸索出了很多解决办法(纯粹是兴趣)。最先想到的前两种
1、将变量 i 保存给在每个段落对象(p)上
1
2
3
4
5
6
7
8
9
|
function
init1() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
pAry[i].i = i;
pAry[i].onclick =
function
() {
alert(
this
.i);
}
}
}
|
2、将变量 i 保存在匿名函数自身
1
2
3
4
5
6
7
8
|
function
init2() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
(pAry[i].onclick =
function
() {
alert(arguments.callee.i);
}).i = i;
}
}
|
后又想到了三种
3、加一层闭包,i 以函数参数形式传递给内层函数
1
2
3
4
5
6
7
8
9
10
|
function
init3() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
(
function
(arg){
pAry[i].onclick =
function
() {
alert(arg);
};
})(i);
//调用时参数
}
}
|
4、加一层闭包,i 以局部变量形式传递给内层函数
1
2
3
4
5
6
7
8
9
10
11
|
function
init4() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
(
function
() {
var
temp = i;
//调用时局部变量
pAry[i].onclick =
function
() {
alert(temp);
}
})();
}
}
|
5、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)
1
2
3
4
5
6
7
8
9
10
|
function
init5() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
pAry[i].onclick =
function
(arg) {
return
function
() {
//返回一个函数
alert(arg);
}
}(i);
}
}
|
后又发现了两种
6、用Function实现,实际上每产生一个函数实例就会产生一个闭包
1
2
3
4
5
6
|
function
init6() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
pAry[i].onclick =
new
Function(
"alert("
+ i +
");"
);
//new一次就产生一个函数实例
}
}
|
7、用Function实现,注意与6的区别
1
2
3
4
5
6
|
function
init7() {
var
pAry = document.getElementsByTagName(
"p"
);
for
(
var
i=0; i<pAry.length; i++ ) {
pAry[i].onclick = Function(
'alert('
+i+
')'
);
}
}
|