JavaScript执行上下文和闭包

时间:2019-07-12 10:47:24

标签: javascript

我试图深入了解闭包。考虑来自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的内容。

2 个答案:

答案 0 :(得分:0)

  

页面上显示

不要使用W3Schools。真。这是一个糟糕的网站,对几乎所有内容的解释都很糟糕。


一个变量存在,直到不再引用它为止。

counterinnerAdd引用,因此counter将一直存在,直到innerAdd不再存在。由于innerAdd是从outerAdd返回并分配给add的,因此它一直存在直到(在您的示例中)程序结束。

  

如果计数器是一个对象怎么办?

counter变量。在您的示例中,它的值是一个数字。如果该值是对对象的引用,则它将以完全相同的方式工作。

答案 1 :(得分:0)

和往常一样,w3schools不太准确。 尝试

的上下文中使用该描述
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?