下面我们将看到的是 JavaScript 中必须提到的功能最强大的抽象概念之一:闭包。但它可能也会带来一些潜在的困惑。那它究竟是做什么的呢?
function makeAdder(a) { return function(b) { return a + b; } } var x = makeAdder(5); var y = makeAdder(20); x(6); // ? y(7); // ?
makeAdder
这个名字本身应该能说明函数是用来做什么的:它创建了一个新的 adder
函数,这个函数自身带有一个参数,它被调用的时候这个参数会被加在外层函数传进来的参数上。
这里发生的事情和前面介绍过的内嵌函数十分相似:一个函数被定义在了另外一个函数的内部,内部函数可以访问外部函数的变量。唯一的不同是,外部函数已经返回了,那么常识告诉我们局部变量“应该”不再存在。但是它们却仍然存在——否则 adder
函数将不能工作。也就是说,这里存在 makeAdder
的局部变量的两个不同的“副本”——一个是 a
等于5,另一个是 a
等于20。那些函数的运行结果就如下所示:
x(6); // 返回 11 y(7); // 返回 27
下面来说说到底发生了什么。每当 JavaScript 执行一个函数时,都会创建一个作用域对象(scope object),用来保存在这个函数中创建的局部变量。它和被传入函数的变量一起被初始化。这与那些保存的所有全局变量和函数的全局对象(global object)类似,但仍有一些很重要的区别,第一,每次函数被执行的时候,就会创建一个新的,特定的作用域对象;第二,与全局对象(在浏览器里面是当做 window
对象来访问的)不同的是,你不能从 JavaScript 代码中直接访问作用域对象,也没有可以遍历当前的作用域对象里面属性的方法。
所以当调用 makeAdder
时,解释器创建了一个作用域对象,它带有一个属性:a
,这个属性被当作参数传入 makeAdder
函数。然后 makeAdder
返回一个新创建的函数。通常 JavaScript 的垃圾回收器会在这时回收 makeAdder
创建的作用域对象,但是返回的函数却保留一个指向那个作用域对象的引用。结果是这个作用域对象不会被垃圾回收器回收,直到指向 makeAdder
返回的那个函数对象的引用计数为零。
作用域对象组成了一个名为作用域链(scope chain)的链。它类似于原型(prototype)链一样,被 JavaScript 的对象系统使用。
一个闭包就是一个函数和被创建的函数中的作用域对象的组合。
闭包允许你保存状态——所以它们通常可以代替对象来使用。这里有一些关于闭包的详细介绍。
原文地址:https://www.cnblogs.com/idzswi/p/9685940.html