为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

潇潇雨雨 2022-12-29 15:22:33
以下 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输出:011因此,似乎首先通过引用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 0l[1]();  // prints 1l[2]();  // prints 2
查看完整描述

2 回答

?
UYOU

TA贡献1878条经验 获得超4个赞

在 JavaScript 中,无论 g() 是否从 f() 内部调用,当 g() 引用表达式中的变量 x 时发生的事情实际上没有区别。只有一个变量x,无论何时运行代码,获取它都是相同的内部操作g()。


JavaScript 与 C++ 有很大的不同;表面上的相似性可能具有欺骗性。此外,在讨论JavaScript 语义时很少使用术语“捕获”(根据我的经验,例如在 Stack Overflow 上),尽管规范在其详尽的描述中使用了它来描述进入范围时发生的情况。这里的相关词是闭包,如“x 在 g() 的闭包中。(我对术语很草率,所以有人可能会改进我的措辞。)


更多:注意,我们可以修改g()来演示,x仍然不仅可以访问获取其值,还可以修改:


    function f() {

      function g() { console.log(x = x + 1); }

      let x = 0;

      g();  // prints 1

      x = 1;

      g();  // prints 2

      return g;

    }

    

    g = f();

    g();

    g();

    g();

变量x继续表现得像普通变量一样。



查看完整回答
反对 回复 2022-12-29
?
慕沐林林

TA贡献2016条经验 获得超9个赞

简而言之

你几乎是正确的,除了它超出范围时它是如何工作的。

更多细节

如何在 JavaScript 中“捕获”变量?

JavaScript 使用词法环境来确定哪个函数使用哪个变量。词法环境由环境记录表示。在你的情况下:

  • 有一个全球环境;

  • 该函数f()定义了它的词法环境,在其中x被定义,即使它在 之后g()

  • 内部函数g()定义了它的词法环境,它是空的。

所以g()使用x. 由于那里没有绑定x,JavaScriptx在封闭环境中查找。由于它在其中找到,因此xing()将使用 in 的x绑定f()。这看起来像词法范围绑定。

如果稍后您在调用x的环境中定义一个,仍然会绑定到in :g()g()xf()

function f() {

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

  let x = 0;

  g();  // prints 0

  x = 1;

  g();  // prints 1

  return g;

}


let x = 4;

let g = f();

g();  // prints 1 (the last known value in f before returning)

在线演示

这表明绑定是静态的,并且将始终在定义x的词法范围内引用已知的g()

这篇优秀的文章用非常漂亮的图形详细解释了它是如何工作的。它适用于闭包(即具有执行上下文的匿名函数),但也适用于普通函数。

为什么超出范围的变量的值会被保留?

如何解释这种非常特殊的行为,即x只要x仍在范围内,JavaScript 将始终采用当前值(如 C++ 中的引用),而在超出范围时它将采用最后一个已知值x(当超出范围的引用时) C++ 将是 UB)?当变量消失时,JavaScript 是否将值复制到闭包中?不,比这更简单!

这与垃圾收集有关:g()返回到外部上下文。由于g()使用了xin f(),垃圾收集器将意识到这个x对象f()仍在使用中。因此,只要g()可访问,xinf()就会保持活动状态,并保持其仍处于活动状态的绑定可访问。所以不需要复制值:x对象将保持不变(未修改)。

作为不是拷贝的证明,你可以研究下面的代码。f()它在能够更改(相同)的上下文中定义了第二个函数x

let h;


function f() {

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

  h = function () { x = 27; }

  let x = 0;

  g();  // prints 0

  x = 1;

  g();  // prints 1

  x = 3;

  return g;

}


let x = 4;

let g = f();

g();  // prints 3

h();

g();  // prints 27

在线演示

编辑:在稍微复杂的上下文中解释这种现象的附加奖励文章。有趣的是,它解释说如果不采取预防措施,这种情况会导致内存泄漏。


查看完整回答
反对 回复 2022-12-29
  • 2 回答
  • 0 关注
  • 50 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信