理解闭包为何能够实现这样的功能,需要了解一些关于JavaScript作用域和内存管理的基本概念。
JavaScript中的作用域和生命周期
在JavaScript中,当函数执行时,会创建一个执行上下文,其中包含该函数的局部变量。通常情况下,当函数执行完毕后,这些局部变量会被垃圾回收机制(Garbage Collector)清理,因为它们不再被引用。
但是,闭包是一种特殊情况。
什么是闭包?
闭包是指那些能够访问其外部函数作用域的函数,即使在外部函数执行完毕之后。这意味着,闭包能够“记住”它在创建时所处的环境(作用域)。
变量为什么没有被删除?
在自调用函数的例子中,虽然自调用函数执行完毕,但返回的内部函数仍然持有对 counter
变量的引用。由于这个引用存在,counter
变量不会被垃圾回收机制清理,从而实现了“私有变量”的效果。
让我们再次看一下代码:
const add = (function() {
let counter = 0; // 计数器变量,处于自调用函数的作用域内
// 返回一个可以访问和修改 counter 的函数
return function() {
counter += 1;
return counter;
};
})();
详细解释:
-
自调用函数:定义并立即执行,创建了一个新的作用域,其中包含变量
counter
。 -
返回的函数:被赋值给
add
,这个返回的函数能够访问counter
,因为它被定义在自调用函数的内部。 -
闭包的创建:当自调用函数执行完毕,返回的函数仍然“记住”了它的创建环境,包括
counter
变量。这就是闭包的形成。
形象的理解
可以把闭包想象成一个外卖订单(闭包),在订单中你指定了一些信息(变量 counter
)。虽然餐厅(自调用函数)已经关门了,但外卖员(返回的函数)还记得你下单时指定的信息,并可以继续处理你的订单。
闭包的工作原理
-
函数执行上下文:当自调用函数执行时,它创建了一个新的执行上下文,包含变量
counter
。 -
变量作用域链:返回的内部函数拥有一个作用域链(scope chain),该链包含其自身的作用域以及外部函数的作用域。在这个例子中,返回的函数可以通过作用域链访问
counter
。 -
引用保持:由于返回的函数持有对
counter
的引用,这个引用使得counter
变量在自调用函数执行完毕后依然存在。
小结
闭包之所以能够让变量在外部函数执行完毕后依然存在,是因为返回的内部函数保持了对这些变量的引用。正是这种引用,确保了变量不会被垃圾回收机制清理,从而实现了“私有”变量的效果。这种特性在许多场景中非常有用,例如数据隐藏、回调函数、事件处理器等。