PS:红色字体为本人的理解。
我们将看到的是javaScript中不能不提到的功能最强大的结构之一:闭包。但它同时也有很多潜在的困扰。那么它究竟是做什么的呢?
function makeAdder(a) {
return function(b) {
return a + b;
}
}
x = makeAdder(5);
y = makeAdder(20);
x(6)
?
y(7)
?
makeAdder
函数的名字自身应该就揭示了很多秘密:它创建了一个新的'adder'函数,这个函数自身带有一个参数,它被调用的时候这个参数会被加在外层函数传进来的参数上。
这里发生的事情和前面介绍过的内嵌函数十分相似:一个函数被定义在了另外一个函数的内部,内部函数可以存取外部函数的变量。这里唯一的不同就是:外部函数被返回了(我的理解:内部函数被外部函数返回了),那么常识告诉我们局部变量“应该”不再存在。但是它们却仍然存在--否则adder函数将不能工作。也就是说,这里存在了makeAdder
的局部变量的两个不同的“副本”--一个是a
等于5,另一个是a
等于20。所以那些函数的结果就如下所示了:
x(6) // 返回 11
因为上面代码中 x = makeAdder(5);实际这里的x是指向function(b){...}的,所以x(6)返回的是return a+b;就是5+6;
y(7) // 返回 27
同上。
这里就是实际发生了的事情。每当javascript执行一个函数的时候,会创建一个'范围对象',并用来保存在这个函数中创建的局部变量。它和被传入函数的变量一同被初始化。这与那些保存的所有全局变量和函数的全局对象(global object)相似,但有一些很重要的不同点是:第一,每次函数被执行的时候,就会创建一个新的,特定的范围对象;第二,与全局对象(在浏览器里面是当做 window 对象来访问的)不同的是,你可以从javascript代码中直接访问范围对象。目前并没有机制可以遍历当前的范围对象里面的属性。
所以当调用makeAdder
时,创建了一个范围对象,它带有一个属性:a
,被当做参数传入makeAdder
函数。makeAdder
然后返回一个新创建的函数。通常JavaScript的垃圾回收器会在这个点上清理掉makeAdder
创建的范围对象,但是返回的函数却保留着一个指向那个范围对象的引用。结果,这个范围对象将不会被垃圾回收器回收,直到指向makeAdder
返回的那个函数对象的引用计数为零。
范围对象组成了一个名为范围链的链。它类似于原形(prototype)链一样,被javascript的对象系统使用。
一个闭包就是一个函数和被创建的函数中的范围对象的组合。可以结合上面的注释来理解,这句话中的‘一个函数’可以看作 function(b){...},‘被创建的函数中的范围对象’可以看作是x(此x非彼x,要区分理解),这两个的组合就构成了一个闭包。
闭包允许你保存状态--所以,它们通常可以代替对象来使用。
其实对闭包的解释通俗点的话就是可以读取其他函数内部变量的函数,因为在JS中函数和变量的作用域相当于链式的(子函数可以使用父函数中的变量,反之不行。),因此,为了能够在函数外部读取函数内部的变量,我们就可以使用闭包。
注:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
转载:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript
http://www.jb51.net/article/24101.htm 整理所得。