闭包的概念:
所谓闭包,即是由函数和与其相关联的引用环境组合而成的实体。
所谓引用环境是指在执行程序中的某个点所有处于活跃状态的约束所组成的集合。其中的约束是指一个变量的名字和其所代表的对象之间的关系。那么为什么要把引用环境和函数结合起来呢?这主要是因为在支持嵌套作用域的语言中,有时不能简单直接的确定函数的引用环境。这样的语言一般有如下特性:
a、函数是第一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
b、函数可以嵌套定义,即在一个函数内部可以定义另一个函数。
上面的叙述比较概念化,下面通过例子来解释闭包:
//code1:
function makePowerFn(power){
function powerFn(base){
return Math.pow(base, power);
}
return powerFn;
}
var square = makePowerFn(2);
alert(square(3)); // return 9
所谓闭包,即是由函数和与其相关联的引用环境组合而成的实体。
所谓引用环境是指在执行程序中的某个点所有处于活跃状态的约束所组成的集合。其中的约束是指一个变量的名字和其所代表的对象之间的关系。那么为什么要把引用环境和函数结合起来呢?这主要是因为在支持嵌套作用域的语言中,有时不能简单直接的确定函数的引用环境。这样的语言一般有如下特性:
a、函数是第一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
b、函数可以嵌套定义,即在一个函数内部可以定义另一个函数。
上面的叙述比较概念化,下面通过例子来解释闭包:
//code1:
function makePowerFn(power){
function powerFn(base){
return Math.pow(base, power);
}
return powerFn;
}
var square = makePowerFn(2);
alert(square(3)); // return 9
来看看我们认为理论上的运行过程:
square = makePowerFn(2);
一般运行完这句话之后makePowerFn就会被JavaScript中的GC【垃圾回收机制】回收。因此makePowerFn的栈已经被销毁
因此再执行square(3),powerFn函数体内有引用power参数,这个时候连makePowerFn都被收回了,power参数自然就不存在了,所以执行时会报错。
但事实上不是这样的。
square = makePowerFn(2);
一般运行完这句话之后makePowerFn就会被JavaScript中的GC【垃圾回收机制】回收。因此makePowerFn的栈已经被销毁
因此再执行square(3),powerFn函数体内有引用power参数,这个时候连makePowerFn都被收回了,power参数自然就不存在了,所以执行时会报错。
但事实上不是这样的。
事实上:
makePowerFn中的powerFn被之外的变量引用时,JavaScript引擎以某种方式将内部函数引用到的变量和参数保存起来。
保存这些值的现象就别成为闭包(Closure)。
makePowerFn中的powerFn被之外的变量引用时,JavaScript引擎以某种方式将内部函数引用到的变量和参数保存起来。
保存这些值的现象就别成为闭包(Closure)。
实际例子的解释:
函数makePowerFn外的变量square引用了函数makePowerFn内的局部函数powerFn,就是说:当makePowerFn的局部函数powerFn被函数makePowerFn外的一个变量引用的时候,就创建了一个闭包。
函数makePowerFn外的变量square引用了函数makePowerFn内的局部函数powerFn,就是说:当makePowerFn的局部函数powerFn被函数makePowerFn外的一个变量引用的时候,就创建了一个闭包。
闭包的生命周期:
在square函数执行完毕后,makePowerFn函数内的参数power与局部函数powerFn并不会被垃圾回收机制回收,我们用代码来说明。
一个普通函数:
//code2:
function makePowerFn(power){
return power;
}
makePowerFn(2);
此时makePowerFn除了返回power没有做任何事情,在执行完毕后,调用堆栈马上被销毁,垃圾回收器会回收执行中在堆栈中生成的makePowerFn函数对象。
在square函数执行完毕后,makePowerFn函数内的参数power与局部函数powerFn并不会被垃圾回收机制回收,我们用代码来说明。
一个普通函数:
//code2:
function makePowerFn(power){
return power;
}
makePowerFn(2);
此时makePowerFn除了返回power没有做任何事情,在执行完毕后,调用堆栈马上被销毁,垃圾回收器会回收执行中在堆栈中生成的makePowerFn函数对象。
闭包函数:
//code3:
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c = a();
var d = a();
c(); // return 1
c(); // return 2
c(); // return 3
d(); // return 1
d(); // return 2
d(); // return 3
//code3:
function a(){
var i=0;
function b(){
alert(++i);
}
return b;
}
var c = a();
var d = a();
c(); // return 1
c(); // return 2
c(); // return 3
d(); // return 1
d(); // return 2
d(); // return 3
我们可以看到c()一共运行3次,每次都加1,为什么呢?按常理函数执行完后调用堆栈就会被销毁,为什么i的变量还会保存在堆栈中?
这就是闭包神奇的力量,在运行时函数已经保存了i,所以递增器b函数可以访问它,而且运行时为每个递增器都保存了一个i的拷贝。
这就是闭包神奇的力量,在运行时函数已经保存了i,所以递增器b函数可以访问它,而且运行时为每个递增器都保存了一个i的拷贝。
是引用而不是复制:
在闭包函数中,内部函数引用外部函数的局部变量是引用的方式,而不是复制,我们来看代码:
//code4:
function a(){
var i = 0;
function b(){
return ++i;
}
++i;
return b;
}
var c = a();
alert(c());// return 2
在闭包函数中,内部函数引用外部函数的局部变量是引用的方式,而不是复制,我们来看代码:
//code4:
function a(){
var i = 0;
function b(){
return ++i;
}
++i;
return b;
}
var c = a();
alert(c());// return 2
闭包的应用:
1、安全性
保护函数内的变量安全。 如code4中的变量i,只有内部函数b才可以访问到,外部无法改变。
2、成员持久性
在内存中维持一个变量。 如code3中,由于闭包,函数a中i一直存在于内存中,因此每次执行c(),都会给i自动加1。
1、安全性
保护函数内的变量安全。 如code4中的变量i,只有内部函数b才可以访问到,外部无法改变。
2、成员持久性
在内存中维持一个变量。 如code3中,由于闭包,函数a中i一直存在于内存中,因此每次执行c(),都会给i自动加1。
[...] 原文地址:http://www.imf7.com/archives/300 « 无觅对《无觅插件背后的阴谋》一文的回应 [...]