当引用在 Javascript 中脱离上下文时,按引用捕获是否会变成按值捕获?

以下 Javascript 程序:


function f() {

  function g() { console.log(x); }

  let x = 0;

  g();  // prints 0

  x = 1;

  g();  // prints 1

  return g;

}


let g = f();

g();  // prints 1

输出:


0

1

1

因此,似乎首先通过引用g捕获(因为在内部,然后在反弹时打印),这意味着闭包环境看起来像,然后通过值(因为在外部,在主体末尾脱离上下文时打印),这意味着闭包环境看起来像.x fg()01xg{'x': x}fg()1xfg{'x': 1}


我试图将此行为与 C++ lambda 相关联,后者提供按引用和按值捕获,但与 Javascript 相反,不允许按引用捕获通过转换为按值捕获(相反,调用lambda 变成未定义的行为)。


这是对 Javascript 捕获的正确解释吗?


如果这个解释是正确的,那将清楚地解释块作用域变量 ( let) 的捕获是如何在for循环中工作的:


let l = [];


for (let x = 0; x < 3; ++x) {

  l.push(function () { console.log(x); });

}


l[0]();  // prints 0

l[1]();  // prints 1

l[2]();  // prints 2


潇潇雨雨
浏览 51回答 2
2回答

UYOU

在 JavaScript 中,无论 g() 是否从 f() 内部调用,当 g() 引用表达式中的变量 x 时发生的事情实际上没有区别。只有一个变量x,无论何时运行代码,获取它都是相同的内部操作g()。JavaScript 与 C++ 有很大的不同;表面上的相似性可能具有欺骗性。此外,在讨论JavaScript 语义时很少使用术语“捕获”(根据我的经验,例如在 Stack Overflow 上),尽管规范在其详尽的描述中使用了它来描述进入范围时发生的情况。这里的相关词是闭包,如“x 在 g() 的闭包中。(我对术语很草率,所以有人可能会改进我的措辞。)更多:注意,我们可以修改g()来演示,x仍然不仅可以访问获取其值,还可以修改:&nbsp; &nbsp; function f() {&nbsp; &nbsp; &nbsp; function g() { console.log(x = x + 1); }&nbsp; &nbsp; &nbsp; let x = 0;&nbsp; &nbsp; &nbsp; g();&nbsp; // prints 1&nbsp; &nbsp; &nbsp; x = 1;&nbsp; &nbsp; &nbsp; g();&nbsp; // prints 2&nbsp; &nbsp; &nbsp; return g;&nbsp; &nbsp; }&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; g = f();&nbsp; &nbsp; g();&nbsp; &nbsp; g();&nbsp; &nbsp; g();变量x继续表现得像普通变量一样。

慕沐林林

简而言之你几乎是正确的,除了它超出范围时它是如何工作的。更多细节如何在 JavaScript 中“捕获”变量?JavaScript 使用词法环境来确定哪个函数使用哪个变量。词法环境由环境记录表示。在你的情况下:有一个全球环境;该函数f()定义了它的词法环境,在其中x被定义,即使它在 之后g();内部函数g()定义了它的词法环境,它是空的。所以g()使用x.&nbsp;由于那里没有绑定x,JavaScriptx在封闭环境中查找。由于它在其中找到,因此xing()将使用 in 的x绑定f()。这看起来像词法范围绑定。如果稍后您在调用x的环境中定义一个,仍然会绑定到in&nbsp;:g()g()xf()function f() {&nbsp; function g() { console.log(x); }&nbsp; let x = 0;&nbsp; g();&nbsp; // prints 0&nbsp; x = 1;&nbsp; g();&nbsp; // prints 1&nbsp; return g;}let x = 4;let g = f();g();&nbsp; // prints 1 (the last known value in f before returning)在线演示这表明绑定是静态的,并且将始终在定义x的词法范围内引用已知的g()。这篇优秀的文章用非常漂亮的图形详细解释了它是如何工作的。它适用于闭包(即具有执行上下文的匿名函数),但也适用于普通函数。为什么超出范围的变量的值会被保留?如何解释这种非常特殊的行为,即x只要x仍在范围内,JavaScript 将始终采用当前值(如 C++ 中的引用),而在超出范围时它将采用最后一个已知值x(当超出范围的引用时) C++ 将是 UB)?当变量消失时,JavaScript 是否将值复制到闭包中?不,比这更简单!这与垃圾收集有关:g()返回到外部上下文。由于g()使用了xin&nbsp;f(),垃圾收集器将意识到这个x对象f()仍在使用中。因此,只要g()可访问,xinf()就会保持活动状态,并保持其仍处于活动状态的绑定可访问。所以不需要复制值:x对象将保持不变(未修改)。作为不是拷贝的证明,你可以研究下面的代码。f()它在能够更改(相同)的上下文中定义了第二个函数x:let h;function f() {&nbsp; function g() { console.log(x); }&nbsp; h = function () { x = 27; }&nbsp; let x = 0;&nbsp; g();&nbsp; // prints 0&nbsp; x = 1;&nbsp; g();&nbsp; // prints 1&nbsp; x = 3;&nbsp; return g;}let x = 4;let g = f();g();&nbsp; // prints 3h();g();&nbsp; // prints 27在线演示编辑:在稍微复杂的上下文中解释这种现象的附加奖励文章。有趣的是,它解释说如果不采取预防措施,这种情况会导致内存泄漏。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript