定义:函数对象可以通过作用域链相互关联起来,函数体内部的变量都可以保存在函数作用域内,这种特性在计算机科学文献中成为“闭包”。
作用域链(scope chain) 的定义:
每一段JavaScript代码(全局代码或函数)都有一个与之关联的作用域链,这个作用域链是一个对象列表或者链表,这组对象定义了这段代码中的变量。当JavaScript需要查找变量x的值的时候(这个过程叫做变量解析(variable resolution)),它会从链的第一个对象开始查找,如果这个对象有一个名为x的属性,则会直接使用这个属性的值,如果第一个对象中不存在名为x的属性,则会继续查找下一个对象,以此类推。
var countInteger = (function f() { //定义函数并立即调用
var counter = 0; //私有变量
return function g(){return counter++;}; //函数f返回的是一个函数(返回的这个函数嵌套在f内)。私有变量保存在函数体作用域内。
}());
console.log(countInteger()); //0
console.log(countInteger()); //1
console.log(countInteger()); //2
我们分析上述的代码:
代码中定义了一个立即执行的函数var countInteger = (xxx); ,而这个立即执行的函数f返回了另外一个函数g,因此,我们最终给变量countInteger赋的值为这个嵌套函数g。这个嵌套函数g是可以访问作用域内的变量的,而且可以访问外部函数中定义的counter变量。当外部函数返回之后,其他任何代码都无法访问counter变量,只有内部的函数才能访问它。
值得注意的是,返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
下面我们来看一个例子,我们想使用闭包,将数字0-9利用闭包存储到数组中:
function constfuncs() {
var funcs = [];
for(var i = 0; i < 10; i++)
funcs[i] = function() {return i; };
return funcs;
}
var funcs = constfuncs();
console.log(funcs[1]()); //返回值是?-->10,并非1
console.log(funcs[5]()); //返回值是?-->10,并非5
上面的这段代码创建了10个闭包,并将它们存储到一个数组中。这些闭包都是在同一个函数调用中定义的,因此它们可以共享变量i。当constfuncs()返回时,变量i的值都是10,所有的闭包都共享这一个值。
我们只要修改一下上述的代码,就可以得到我们想要的结果:
function constfuncs() {
var funcs = [];
for(var i = 0; i < 10; i++)
funcs[i] = (function(n) { return function(){
return n;
}}(i));
return funcs;
}
var funcs = constfuncs();
console.log(funcs[1]()); //返回值是?-->1
console.log(funcs[5]()); //返回值是?-->5