先看一下代码示例:
function f(){
var cnt=0;
return function(){ return ++cnt;}
}
var fa=f();//将函数f的的返回值给变量fn
// fa(); //对fn的函数调用
console.log(fa());//1
console.log(fa());//2
console.log(fa());//3
函数的返回值是函数(对象的引用),这里将其赋值给变量fn。在调用fn时,其输出结果每次都会自增加1
从表面看,闭包(closure)具有状态的函数,或者也可以将闭包的特征理解为:其相关的局部变量在函数调用结束后会继续存在
一、闭包的原理
1.1 嵌套的函数声明:
闭包的前提条件是需要在函数声明的内部声明另一个函数(即嵌套的函数声明)贴一下函数函数声明的simple example:
function f(){
function g(){
console.log('g is called');
}
g();
}
f()// g is called
在函数f的声明中包含函数g的声明以及调用语句。再调用函数f时,就间接地调用了函数g。为了更好理解该过程,在此对其内部机制进行说明。
在javaScript中,调用函数时将会隐式地生成call对象。为了方便起见,我们将调用函数f生成的call对象称作call-f对象。在函数调用完成之后,call对象将被销毁。
函数f内的函数g的声明将会生成一个与函数的g相对应function对象。其名称g是call-f对象的属性。由 于每一次调用函数都会独立生成call对象,因此在调用函数g时将会隐式地生成另一个call对象。为了方便起见,我们将该call对象称作call-g对象。
离开函数g之后,call-g对象将被自动销毁。类似的,离开函数f之后,call-f对象也就自动销毁。此时,由于属性g将与call-g对象一起被销毁,所以由g所引用的function对象将会失去其引用,而最终(通过垃圾回收机制)被销毁。
1.2嵌套函数与作用域
对上面代码稍稍修改:
function f(){
var n=123;
function g(){
console.log("n is"+n);
console.log('g is called');
}
g();
}
f();
运行结果:
js>f();
n is 123
g is called'
在内层进行声明函数g可以访问外层的函数f的局部变量(在这里指变量n),对于嵌套声明的函数,内部的函数将会首先查找被调用时所生成的call对象的属性,之后之后在查找外层函数的call对象的属性。这一机制被称为作用链。
1.3嵌套函数的返回
上面的代码稍稍修改
function f(){
var n=123;
function g(){
console.log("n is"+n);
console.log('g is called');
}
return g;
}
js> f();
function g(){
console.log("n is"+n);
console.log('g is called');
}
由于return语句,函数将会返回一个function对象(的引用)。调用函数f的结果是一个function对象。这时,虽然会生成与函数f相对应的call对象(call-f对象)(并在离开函数f后被销毁),但由于不会调用函数g,所以此时还不会生成与之相对应的call对象(call-g对象),请对此多加注意。
1.4闭包
js>var g2=f();
jd>g2();
n is 123//将返回的函数赋值给变量
g is called//调用函数(函数f内函数g)