定义
先看看《JavaScript高级程序设计》(第3版)中对闭包的定义:
闭包是指一个有权访问另一个函数作用域中的变量的函数。
既然如此,那闭包它首先是一个函数,其次它还具备访问另一个函数作用域内变量的能力。
先做个铺垫
下面代码初衷:执行一个数组元素时输出该数组的下标值。
function F(){
var arr = [],i;
for(i=0;i<3;i++){
arr[i] = function(){
return i;
}
}
return arr;
}
var arr = F();
console.log(arr[0]());//3
console.log(arr[1]());//3
console.log(arr[2]());//3
通过验证,发现以上代码并没有达到初衷。那么又是什么原因导致每次执行都会输出3呢?
原因是当执行完 var arr = F(); 的时候, i 已经从for循环递增到3了。所以当每次执行数组元素中的函数想输出该元素的下标时,总会输出3。
使用闭包解决上述问题
function F(){
var arr = [],i;
for(i=0;i<3;i++){
arr[i] = (function(x){
return x;
})(i);
}
return arr;
}
var arr = F();
console.log(arr[0]());//0
console.log(arr[1]());//1
console.log(arr[2]());//2
对比一下,使用一个即时函数就可以解决问题。分析一下:第一个例子没有实现预期效果的原因是虽然创建了三个闭包,但是返回值只是 i 的引用,既然是引用,所以每次返回的都将是同一个值–3;
同理,第二个例子能保存i值的原因是每次进入for循环后即时函数都创建了不用的局部变量,每次的局部变量则记录每次for循环的值,因此当执行数组元素函数时便能访问到不同的值。
常见的应用场景
下面代码初衷:点击一个div输出该div的类数组对象index值。
<html>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script type="text/javscript">
var nodes = document.getElementsByTagName("div");
for(var i =0;i<nodes.length;i++){
nodes[i].addEventListener("click",function(){
console.log(i);
});
}
</script>
</body>
</html>
再一次地,点击div依旧没有输出我们想要的结果。下面利用闭包来解决:
<html>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<script type="text/javscript">
var nodes = document.getElementsByTagName("div");
for(var i =0;i<nodes.length;i++){
(function(x){
nodes[i].addEventListener("click",function(){
console.log(x);
});
})(i);
};
</script>
</body>
</html>
文章说明
参考资料文献:《JavaScript高级程序设计》(第3版)
《JavaScript面向对象编程指南》(第2版)
水平有限,欢迎指正。