例一
var data = [];
for(var k = 0; k < 3; k++){
data[k] = function(){
return k;
};
}
alert(data[0]());
预想中应输出0,但结果为3
首先必须清楚一下几点
- . 在JS中没有块级作用域的概念,for循环中定义的var k实际上是一个window全局变量。
- . 内部环境可以通过作用域链访问所有的外部环境,但外部环境不能访问内部环境中的任何变量和函数。即每个环境都可以向上搜索作用域链,以查询变量和函数;但任何环境不能向下搜索作用域链而进入另一环境。
- .
在这段代码中,执行data[0]()
时,调用函数return k赋值给data[k],在这个函数作用域中没有定义k,因此向上搜索找到了全局变量k。由于每次循环更改的都是同一个空间的k,当循环执行到K=3,由于看<3不成立循环里面的代码不执行,但是k所在空间的值已经为3,因此当调用data[0]()
时,return K执行后所赋的值就是3.
解决这个问题的方式就是引入闭包
//加一层闭包,i以函数参数形式传递给内层立即执行函数
for(var k = 0; k < 3; k++){
data[k] = (function(){
return k;
})(k);
}
还有下面的(此处引用一些网上的方法)
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>闭包演示</title>
</head>
<body>
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<script type="text/javascript">
window.onload=function() {
var ps = document.getElementsByTagName("p");
for( var i=0; i<ps.length; i++ ) {
ps[i].onclick = function() {
alert(i);
}
}
}
</script>
</body>
</html>
点击每个段落都弹出5,原因同上,都是用的同一个内存空间的i
提供一下解决方案
//sava1:加一层闭包,i以函数参数形式传递给内层函数
for( var i=0; i<ps.length; i++ ) {
(function(arg){
ps[i].onclick = function() {
alert(arg);
};
})(i);//调用时参数
}
//save2:加一层闭包,i以局部变量形式传递给内存函数
for( var i=0; i<ps.length; i++ ) {
(function () {
var temp = i;//调用时局部变量
ps[i].onclick = function() {
alert(temp);
}
})();
}
//save3:加一层闭包,返回一个函数作为响应事件(注意与3的细微区别)
for( var i=0; i<ps.length; i++ ) {
ps[i].onclick = function(arg) {
return function() {//返回一个函数
alert(arg);
}
}(i);
}
//save4:将变量 i 保存给在每个段落对象(p)上
for( var i=0; i<ps.length; i++ ) {
ps[i].i = i;
ps[i].onclick = function() {
alert(this.i);
}
}
//save5:将变量 i 保存在匿名函数自身
for( var i=0; i<ps.length; i++ ) {
(ps[i].onclick = function() {
alert(arguments.callee.i);
}).i = i;
}
}
//save6:用Function实现,实际上每产生一个函数实例就会产生一个闭包
for( var i=0; i<ps.length; i++ ) {
ps[i].onclick = new Function("alert(" + i + ");");//new一次就产生一个函数实例
}
//save7:用Function实现,注意与6的区别
for( var i=0; i<ps.length; i++ ) {
ps[i].onclick = Function('alert('+i+')');
}