《javascript面向对象编程指南》——闭包
闭包:有权访问另一个函数作用域中的变量的函数,这样的话每个函数都可以被认为是一个闭包。但是大多数时候,该作用域在函数体执行完之后就自行销毁了
如果一个函数会在其父级函数返回之后留住对父级作用域链的话,相关闭包就会创建
首先是一个一般函数
var a = "global variable";
var F = function() {
var b = "local variable";
var N = function() {
var c = "inner local";
};
};
从全局到最里面的作用域链依次是:
- a、F()
- b、N()
- c
现在将N的空间扩展到F以外,并止步于全局空间以内,就产生闭包
var a = "global variable";
var F = function() {
var b = "local variable";
var N = function() {
var c = "inner local";
return b;
};
return N;
};
var inner = F();
inner(); //"local variable"
如上,只需在F空间return出N空间,然后将F()的调用赋值给全局变量inner,此时inner就是N函数,调用一下inner(),就相当于执行N(),但是这里N(),既可以访问其原有的作用域,也是全局函数
上述步骤就是突破作用域链
下面这种闭包,不是F()返回函数,而是直接在函数体内N赋值给全局变量inner
var inner;
var F = function() {
var b = "local variable";
var N = function() {
return b;
};
inner = N;
};
F(); //需要调用,才能将N赋值给inner
inner(); //"local variable"
相关定义与闭包
如果一个函数会在其父级函数返回之后留住对父级作用域链的话,相关闭包就会创建
function F(param) {
var N = function() {
return param;
};
param++;
return N;
}
var inner = F(123);
inner(); //124
如上,当返回的函数被调用时,param++已经执行过一次递增操作了,所以inner()返回的是被更新后的值
由此,函数所绑定的是作用域本身,而不是在函数定义时该作用域中的变量或变量当前返回的值
循环中的闭包
如下,是一个三次循环操作,每次迭代中都会创建一个返回当前循环序号的新函数
function F() {
var arr = [],
i;
for (i = 0; i < 3; i++) {
arr[i] = function() {
return i;
};
}
return arr;
}
var newArr = F();
console.log(newArr[0]()); //3
console.log(newArr[1]()); //3
console.log(newArr[2]()); //3
如上,结果都是3
我们在这里创建了三个闭包,而他们都指向了一个共同的局部变量i。但是,如上节所说,闭包并不会记录他们的值,他们所拥有的只是相关域在创建时的一个连接(引用)
当循环结束时i的值为3,所以这三个函数都指向这一个共同值(注意不是2)
解决1
function F() {
var arr = [],
i;
for (i = 0; i < 3; i++) {
arr[i] = (function(x) {
return function() {
return x;
};
})(i);
}
return arr;
}
var newArr = F();
console.log(newArr[0]());
console.log(newArr[1]());
console.log(newArr[2]());
如上,将i传递给另一个即时函数
解决2
function F() {
function binder(x) {
return function() {
return x;
};
}
var arr = [],
i;
for (i = 0; i < 3; i++) {
arr[i] = binder(i);
}
return arr;
}
var newArr = F();
console.log(newArr[0]());
console.log(newArr[1]());
console.log(newArr[2]());
如上,定义一个内部函数,将i本地化