有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4。
<!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_r("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.onclick=function(){alert(i);}方式给元 素添加点击事件。响应函数function(){alert(i);}中的 i 并非每次循环时对应的 i(如0,1,2,3,4)而是循环后最后 i 的值5。 或者说循环时响应函数内并未能保存对应的值 i,而是最后一次i++的值5。
了解了原因,摸索出了很多解决办法(纯粹是兴趣)。最先想到的前两种
1、将变量 i 保存给在每个段落对象(p)上
unction
init1() {
??
var
pAry =
document.getElementsByTagName_r(
"p"
);
??
for
(
var
i=0; i<pAry.length; i++ )
{
?????
pAry[i].i = i;
?????
pAry[i].onclick
=
function
() {
????????
alert(
this
.i);
?????
}
??
}
}
2、将变量 i 保存在匿名函数自身
function
init2() {
??
var
pAry =
document.getElementsByTagName_r(
"p"
);
??
for
(
var
i=0; i<pAry.length; i++ )
{
???
(pAry[i].onclick =
function
() {
????????
alert(arguments.callee.i);
????
}).i
= i;
??
}
}
后又想到了三种
3、加一层闭包,i 以函数参数形式传递给内层函数
function
init3() {
??
var
pAry =
document.getElementsByTagName_r(
"p"
);
??
for
(
var
i=0; i<pAry.length; i++ )
{
???
(
function
(arg
){
???????
pAry[i].onclick
=
function
() {
??????????
alert(arg
);
???????
};
???
})(i);
//调用时参数
??
}
}
4、加一层闭包,i 以局部变量形式传递给内层函数
function
init4() {
??
var
pAry =
document.getElementsByTagName_r(
"p"
);
??
for
(
var
i=0; i<pAry.length; i++ )
{
????
(
function
() {
??????
var
temp =
i;
//调用时局部变量
??????
pAry[i].onclick
=
function
() {
????????
alert(temp);
??????
}
????
})();
??
}
}
5、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)
?
function
init5() {
??
var
pAry =
document.getElementsByTagName_r(
"p"
);
??
for
(
var
i=0; i<pAry.length; i++ )
{
???
pAry[i].onclick =
function
(arg) {
???????
return
function
() {
//返回一个函数
???????
alert(arg);
?????
}
???
}(i)
;
??
}
}
后又发现了两种
6、用Function实现,实际上每产生一个函数实例就会产生一个闭包
function
init6() {
????
var
pAry =
document.getElementsByTagName_r(
"p"
);
????
for
(
var
i=0; i<pAry.length; i++ )
{
??????
pAry[i].onclick
=
new
Function(
"alert("
+ i +
");"
);
//new一次就产生一个函数实例
????
}
}
7、用Function实现,注意与6的区别
function
init7() {
????
var
pAry =
document.getElementsByTagName_r(
"p"
);
????
for
(
var
i=0; i<pAry.length; i++ )
{
?????????
pAry[i].onclick
= Function(
'alert('
+i+
')'
);
????
}
}
New Function() 与
Function()的区别:
情景一:
var yx01 = new function() {return "圆心"};
alert(yx01);
我们运行情景一代码,将返回显示“[object object]
”
,此时该代码等价于:
function 匿名类(){
return "圆心";
}
var yx01 = new 匿名类();
alert(yx01);我们对情景一的代码进行下面改造:
var yx01 = new function() {return new String("圆心")};
alert(yx01);
我们运行,将会发现返回的是“圆心”,这是为什么呢?
只 要 new
表达式之后的 constructor
返回(return)一个引用对象(数组,对象,函数等),都将覆盖new创建的匿名对象,如果返回(return)一个原始类型(无
return 时其实为 return 原始类型 undefined),那么就返回 new 创建的匿名对象
由于 new String 会构造一个对象,而不是一个 string 直接量,且new String(x)
如果带参数,那么alert它的时候就会返回 x。所以 yx01 将返回 new String(”圆心”) 这个对象,而 alert
yx01 则显示 “圆心”。
情景二:
var yx02 = function() {return "圆心"}();
alert(yx02);我们运行情景二代码,将返回显示“圆心”,此时该代码等价于:
var 匿名函数 =
function() {return "圆心"};
yx02 =
匿名函数();
alert(yx02);很明显,yx02 返回的是匿名函数的执行结果值,即 yx02 为:“圆心”。
当然匿名函数的执行结果也可以为一个匿名对象。具体常见应用可以看《Javascript的一种模块模式
》