(1)闭包是一个函数的局部变量 - 函数返回后它还活着
(2)闭包是一个函数,返回时内存堆栈帧不会被释放,不会被垃圾回收。
第一个例子:
function sayHello(name){
var text='Hello'+name; //local variable局部变量
var sayAlert=function() {
alert(text);
}
return sayAlert;
}
var say2=sayHello("World");
say2();
上面的sayAlert是一个sayHello函数的局部变量,sayHello返回的是sayAlert,传给了 say2,sayAlert 和say2 实际指向也就是Hold的是同一个函数,这个函数在这里是一个匿名函 数 function() { alert(text); },因为这个匿名函数被外部Hold住了,所以它不 会被垃圾回收,不会从内存中消失。因为这个匿名函数存在sayHello中,是其一部分,因此sayHello也不会消失,因而sayHello中任何元 素都不会消失,比如sayHello中一个局部变量var text就不会消失。这点是与我们一般对象的方法认识不同的,只要是对象方法中的局部变量,该方法被调用完就丢弃,从内存中消失。
何以见得sayHello中一个局部变量var text不会消失呢?因为我们在上面最后一行调用say2()时,并没有任何赋值行为,但是调出的效果还是Hello Jdon,而不是Hello null。因为调用say2(),实际就是执行匿名函数 function() { alert(text); },而alert(text)中调用本地变量text的值,text的值是Hello Jdon。注意的是,这个text属于一旦被赋值就不再改变,是一个不变性质的量,在Java语言中是要用final修饰符的。
当你在一个函数中又使用了另外一个函数时,你在创建一个闭包。
深入案例
为了了解上面闭包造成var text被Hold在内存中的效果,再看源码,有两个函数foo和foo2:
function foo(x) {
var tmp = 3;
return function (y) {
alert(x + y + (++tmp));
}
}
var bar = foo(2); // bar现在是一个闭包
bar(10);
bar(10);
bar(10);
上面我们执行了三次bar(10),结果是16 17 和18,每次结果不一样,因为var tmp这个变量一直存在内存,++tmp是每次累计上次的值,自增。所以,结果不断累积+1。
下面foo2函数不是一个闭包,无论调用几次结果总是16,因为tmp局部变量每次在函数被调用时都是从3开始。
function foo2(x) {
var tmp = 3;
function bar2(y) {
alert(x + y + (++tmp));
}
bar2(10);
}
foo2(2);
foo2(2);
foo2(2)
函数用作全局变量
由于一个函数中的局部变量生存周期只是在一个函数体内,如果我们想将这个局部变量变成全局变量,那么通过函数方法实现对这个局部变量的操作即可,按照面向对象的思路来看,这个局部变量实际是一个可变变量,是一个状态,必须通过自身的方法来操作它,而不是直接将其暴露给外界,任由外界作为全局变量蹂躏。
下面setupSomeGlobals代码是通过几个函数改变其内部变量num,如果你想给num增加,那么调用gIncreaseNumber方法,然后再调用gAlertNumber(),看看结果是否增加。
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 666;
// Store some references to functions as global variables
gAlertNumber = function() { alert(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}
gAlertNumber();
gIncreaseNumber();
gAlertNumber();
gSetNumber(-100);
gAlertNumber();