我试图深入了解闭包。考虑来自w3schools的以下示例:
var add = (function outerAdd() {
var counter = 0;
return function innerAdd() {
counter += 1;
return counter
}
})();
console.log(add());
console.log(add());
console.log(add());
// the counter is now 3
页面上说:“局部变量寿命短。它们在调用函数时创建,并在函数完成时删除。这意味着在运行外部自调用功能counter
之后,outerAdd
被删除。
但是,由于定义(或执行)innerAdd
时形成的作用域链,内部返回的函数counter
仍然能够访问innerAdd
。所以现在的问题是,在创建新的执行上下文时,作用域链是否会复制变量值?因为如果作用域链仅维护指向counter
的指针,则在counter
函数完成运行之后,由于outerAdd
被删除,它应该引发错误。
另外,如果counter是一个对象怎么办?那么“复制”行为将如何工作?在JavaScript中通过引用复制对象。
编辑:我感谢这里的所有答案。但是我真正想理解的是闭包的内部工作,如execution contexts and scope chains所述。因此,我正在寻找一种解释。其他Stackoverflow答案并不能真正解释这种情况在内部如何发生?
就像我不明白counter
的作用域链何时确切引用innerAdd
一样?如果是在执行innerAdd
期间(即为innerAdd
形成执行上下文时),那么为什么当时counter
没有被垃圾回收呢?一旦outerAdd
完成执行,就没有引用counter
的内容。
答案 0 :(得分:0)
页面上显示
不要使用W3Schools。真。这是一个糟糕的网站,对几乎所有内容的解释都很糟糕。
一个变量存在,直到不再引用它为止。
counter
被innerAdd
引用,因此counter
将一直存在,直到innerAdd
不再存在。由于innerAdd
是从outerAdd
返回并分配给add
的,因此它一直存在直到(在您的示例中)程序结束。
如果计数器是一个对象怎么办?
counter
是变量。在您的示例中,它的值是一个数字。如果该值是对对象的引用,则它将以完全相同的方式工作。
答案 1 :(得分:0)
function add() {
var counter = 0;
counter += 1;
}
其中counter
是局部变量,也是,一旦完成对add
的调用,因为其他任何东西都不可能再引用该内部counter
变量了,很快就会收集垃圾。但是,正如您的问题中的代码所示,仅仅因为变量是局部变量并不意味着它不再可引用/将被删除。在大多数情况下,只有其他任何东西都无法引用该变量,它才会被垃圾回收(从内存中删除)。
在您的innerAdd
函数运行时,不会复制值-相反,IIFE范围内的旧counter
变量只是继续存在,因为{{ 1}}仍然可以查看innerAdd
变量,因此counter
变量不会被GC。
另外,如果counter是一个对象怎么办?那么“复制”行为将如何工作?在JavaScript中通过引用复制对象时。
不管变量是原始变量还是对象,闭包和垃圾回收都以相同的方式工作,例如,如果counter
是一个数组,则在每次调用时被推入该数组,则同一件事发生:
counter
只要有人可以引用它,它就会保留在内存中。
有关对象和基元之间的区别,请参见Is JavaScript a pass-by-reference or pass-by-value language?。