2 回答
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
继续表现得像普通变量一样。
TA贡献2016条经验 获得超9个赞
简而言之
你几乎是正确的,除了它超出范围时它是如何工作的。
更多细节
如何在 JavaScript 中“捕获”变量?
JavaScript 使用词法环境来确定哪个函数使用哪个变量。词法环境由环境记录表示。在你的情况下:
有一个全球环境;
该函数
f()
定义了它的词法环境,在其中x
被定义,即使它在 之后g()
;内部函数
g()
定义了它的词法环境,它是空的。
所以g()
使用x
. 由于那里没有绑定x
,JavaScriptx
在封闭环境中查找。由于它在其中找到,因此x
ing()
将使用 in 的x
绑定f()
。这看起来像词法范围绑定。
如果稍后您在调用x
的环境中定义一个,仍然会绑定到in :g()
g()
x
f()
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()
使用了x
in f()
,垃圾收集器将意识到这个x
对象f()
仍在使用中。因此,只要g()
可访问,x
inf()
就会保持活动状态,并保持其仍处于活动状态的绑定可访问。所以不需要复制值: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
编辑:在稍微复杂的上下文中解释这种现象的附加奖励文章。有趣的是,它解释说如果不采取预防措施,这种情况会导致内存泄漏。
添加回答
举报