function F(){
var arr = [],i;
for(i = 0;i < 3;i++){
arr[i] = function(){
return i;
};
}
return arr;
}
var arr = F();
上述for
循环每迭代一次就创建一个arr
的项,每一项中保存着一个函数,该函数返回当前作用域中的i
的变量值,然后在F()
中返回该数组,将该函数赋值就可以访问F()
内部非变量,因此形成了一个闭包
执行arr[0](),arr[1](),arr[2]()
查看结果的时候发现结果并不是我们想象的0,1,2
arr[0]();
3
arr[1]();
3
arr[2]();
3
原因:在for
循环中创建了三个闭包,而它们都指向了一个局部变量i
。但是,闭包并不会记录它们的值,它们所拥有的只是相关域在创建时的一个链接(即引用)。在这个列子中,变量i
存在于定义这三个函数域中。对这三个函数中的任何一个而言,当要去获取某个变量时,它会从其所在的域开始逐级寻找那个距离最近的i
的值。由于循环结束时i
的值为3,所以这三个函数都指向了这个值
换句话说就是:for
循环一次创建一个对i
变量引用的闭包,但是闭包中不会保存当时的值,而只是一个对于i
变量的引用,调用引用的值会随着本身的值得变化而变化,当调用三个函数时会逐级寻找最近的i
的值,此时i
的值为3
改正
换一种闭包的形式
function F(){
var arr = [],i;
for(i = 0;i < 3;i++){
arr[i] = (function(x){
return function(){
return x;
}
}
)(i);
}
return arr;
}
var arr = F();
结果:
arr[0]();
0
arr[1]();
1
arr[2]();
2
在这里,不再创建直接返回i
的函数,而是将i
传递给另一个即时函数。在该函数中,i
被赋值给了局部变量x
,这样一来,每次迭代中的x
就会是不同的值了
若不传值进入闭包内,但是闭包内部对变量i
进行了本地化,则就进行了声明,只是未对其赋值,所以调用结果为undefined
function F(){
var arr = [],i;
for(i = 0;i < 3;i++){
arr[i] = (function(i){
return function(){
return i;
}
}
)();//此处将外部变量传入闭包
}
return arr;
}
var arr = F();
arr[0]();
undefined//未传值进入,所以结果为undefined
若闭包内部变量没有进行变量”本地化操作”,则无论外部是否给予传值,闭包在本地内找不到该变量,便会向上级作用域中查找,若找到则输出该值,未找到则进行报错
传入外部变量
function F(){
var arr = [],i;
for(i = 0;i < 3;i++){
arr[i] = (function(){//未本地化
return function(){
return i;
}
}
)(i);
}
return arr;
}
var arr = F();
arr[0]();
3//向上查找变量i的值
不传入外部变量
function F(){
var arr = [],i;
for(i = 0;i < 3;i++){
arr[i] = (function(){//未本地化
return function(){
return i;
}
}
)();//不传入外部变量
}
return arr;
}
var arr = F();
arr[0]();
3//向上查找变量i的值
未找到变量
function F(){
var arr = [],i;
for(i = 0;i < 3;i++){
arr[i] = (function(){
return function(){
return x;
}
}
)(i);
}
return arr;
}
var arr = F();
arr[0]();
VM895:6 Uncaught ReferenceError: x is not defined
at Array.<anonymous> (<anonymous>:6:24)
at <anonymous>:14:7
下面的形式也可以:
function F(){
var arr = [],i;
for(i = 0;i < 3;i++){
(function(j){
arr[j] = function(){
return j;
};
})(i);
}
return arr;
}
var arr = F();
arr[0]();
0
arr[1]();
1
arr[2]();
2
也可以不使用即时函数,而定义一个内部函数来实现相同的功能,要点是在每次迭代操作中,我们要在中间函数内将i
的值”本地化”而不是当前作用域中的值
function F(){
var arr = [],i;
function binder(x){
return function(){
return x;
};
}
for(i = 0;i < 3;i++){
arr[i] = binder(i);
}
return arr;
}
var arr = F();
arr[0]();
0
arr[1]();
1
arr[2]();
2
若在内部函数中不将其本地化,则结果还是为3
function F(){
var arr = [],i;
function binder(){//未本地化
return function(){
return i;//向上层作用域中查找该变量的值
};
}
for(i = 0;i < 3;i++){
arr[i] = binder();
}
return arr;
}
var arr = F();
arr[0]();
3
但是只要有参数传入内部函数binder()
,就不会再F()
的作用域中寻找i
,会首先在函数作用域中寻找
function F(){
var arr = [],i;
function binder(i){
return function(){
return i;
};
}
for(i = 0;i < 3;i++){
arr[i] = binder(i);
}
return arr;
}
var arr = F();
arr[0]();
0