【问题标题】:Why and when an unexecuted closure can capture outer variable in javascript?为什么以及何时未执行的闭包可以捕获 javascript 中的外部变量?
【发布时间】:2020-01-03 09:33:22
【问题描述】:

我不明白为什么一个未执行的闭包可以捕获外部变量。

我确实读过一些关于execute contextlexical environmentmemory management 的文章,但这些都不能解决我的问题:

function foo() {
  var a = 1;
  return function() {
    console.log(a);
  }
}
var f = foo() // line 7
// HERE variable a was been captured

f = undefined // line 10
// HEAE variable a was been released

当引擎执行到第7行时,foo execution contextglobal execution context之上创建,但在第7行之后,闭包从未被执行,因此闭包执行上下文从未被创建,词法环境也是如此。 foo execution context被弹出,变量a将被释放。

我找不到我的观点有什么问题。 那么为什么以及何时捕获闭包中的变量呢?

【问题讨论】:

  • 如果f() 被调用,你预计什么时候会捕获变量?
  • 闭包是在你调用foo()时创建的。它怎么知道你永远不会使用它?
  • @Bergi 再问一个问题:ExecutionContext 是在函数调用时还是函数声明时创建的?
  • @Barmar ExecutionContext 在函数调用时或函数声明时创建?
  • @TaoPaipai 调用函数时会创建执行上下文(“堆栈帧”)。但是在创建函数时,会在函数对象上捕获词汇环境(“范围”)。

标签: javascript node.js garbage-collection closures v8


【解决方案1】:

关键是词法作用域

词法作用域是 JavaScript 语言使用的作用域模型,它不同于其他一些使用动态作用域的语言。词法范围是在词法分析时定义的范围。

考虑到这一点:

var a = 1
console.log(a)

console.log(b)
var b = 2

console.log(c)

你可以得到结果:

1
undefinded
ReferenceError: c is not defined

因此,您可以看到 JavaScript 如何处理变量:所有变量都在词法分析时定义,并在运行时赋值。这就是他们所谓的吊装

回到问题:闭包在 js 引擎读取您的代码并定义变量并绑定它们的词法分析时捕获变量。

阅读更多关于编译的信息:https://v8.dev/blog/background-compilation

【讨论】:

  • 编译时间不一定和定义时间一样。闭包在创建时捕获变量,而不是在编译其主体时。
  • @Bergi 所说的。如果不是完全错误的话,这个答案是非常具有误导性的。闭包是函数值的运行时表示。每次计算函数表达式时都会创建它。
【解决方案2】:

这是与您的问题相关的关于 bugs.chromium 的讨论。据此,即使 else 块从未执行,对函数中使用的外部对象的引用 由 else 块返回并创建并存储在堆中。它永远不会被垃圾收集。参考下面的讨论:-

https://bugs.chromium.org/p/chromium/issues/detail?id=315190

【讨论】:

    【解决方案3】:
    function foo() {
      var a = 1;
      return function() {
        console.log(a);
      }
    }
    

    函数内部的函数称为闭包。
    当一个函数内部有另一个函数,并且在这种情况下说顶级函数有一些像“a”这样的数据,那么所有内部函数都可以访问该数据,只有当内部函数对这些变量有一些引用时才会发生这种情况.

    假设您在“a”旁边有“b”变量,并且您在内部函数的任何地方使用它,这将被 javascript 简单地忽略。 'Closure' 不会像 'a' 那样保持这个值。

    这实际上是闭包赋予开发者力量的地方。
    查看以下示例 -

    const test = (num1) => (num2) => console.log(num1*num2);//closure is in action here
    var s = test(100)
    s(2); // this will give output of 200
    

    希望这会有所帮助。

    谢谢。

    【讨论】:

      猜你喜欢
      • 2019-06-16
      • 2019-10-14
      • 1970-01-01
      • 2015-08-14
      • 1970-01-01
      • 1970-01-01
      • 2014-01-10
      • 2016-02-13
      相关资源
      最近更新 更多