我想知道局部变量是如何在javascript中分配内存的。 在C和C ++中,局部变量存储在堆栈中。它在javascript中是一样的吗?或者一切都存储在堆中?
答案 0 :(得分:52)
这实际上是一个非常有趣的Javascript领域。 the spec中的详细信息,但是:Javascript处理局部变量的方式与C的方式完全不同。当你调用一个函数时,除此之外还会创建一个用于该调用的“变量环境”,它具有一个称为“绑定对象”的东西。 (简称为“变量对象”;说“变量环境的绑定对象”只是一个啰嗦的 tad 位!)变量对象具有属性对于函数的参数,函数中声明的所有局部变量,以及函数内声明的所有函数(以及其他一些东西)。首先根据变量对象检查函数中的非限定引用(例如,foo
中的foo
,而不是obj.foo
)以查看它们是否与其匹配;如果他们这样做,就会使用这些属性。
当一个闭包在函数返回时存活(由于多种原因可能发生)时,该函数调用的变量对象在闭包中引用的内存中保留。乍一看,这表明堆栈不用于局部变量;实际上,现代JavaScript引擎非常智能,并且可能(如果它值得)将堆栈用于闭包实际上不使用的本地。 (当然,堆栈仍然用于跟踪返回地址等。)
以下是一个例子:
function foo(a, b) {
var c;
c = a + b;
function bar(d) {
alert("d * c = " + (d * c));
}
return bar;
}
var b = foo(1, 2);
b(3); // alerts "d * c = 9"
当我们调用foo
时,会使用以下属性创建变量对象:
a
和b
- 函数的参数c
- 函数bar
- 在函数当foo
执行语句c = a + b;
时,它会引用变量对象上的c
,a
和b
属性,以便调用{{1} 1}}。当foo
返回对其中声明的foo
函数的引用时,bar
将继续调用bar
返回。由于foo
对bar
的特定调用的变量对象具有(隐藏)引用,因此变量对象存活(而在正常情况下,它将没有未完成的引用,因此可用于垃圾收集)。
稍后,当我们调用foo
时,会创建一个用于该调用的 new 变量对象(其中包括)一个名为bar
的属性 - d
的参数1}}。 bar
中的非限定引用首先针对该调用的变量对象进行检查;例如,bar
解析为变量对象的d
属性,用于调用d
。但是,对于bar
的“范围链”中的下一个变量对象,检查与其变量对象上的属性不匹配的非限定引用,该bar
是调用foo
的变量对象。 。由于它有一个属性c
,这就是bar
中使用的属性。例如,粗略地说:
+----------------------------+ | `foo` call variable object | | -------------------------- | | a = 1 | | b = 2 | | c = 3 | | bar = (function) | +----------------------------+ ^ | chain | +----------------------------+ | `bar` call variable object | | -------------------------- | | d = 3 | +----------------------------+
实现可以自由使用他们想要的任何机制来使上面的看起来发生。对于函数调用,不可能直接访问变量对象,并且规范明确指出,如果变量对象只是一个概念,而不是实现的文字部分,那就完全没问题了。一个简单的实现可能只是字面上做的规范说;当没有涉及闭包时(为了速度利益),更复杂的一个可以使用堆栈,或者可以总是使用堆栈但是在弹出堆栈时“撕掉”闭包所需的变量对象。在任何特定情况下,唯一知道的方法是查看他们的代码。 : - )
有关闭包,范围链等的更多信息,请点击此处:
答案 1 :(得分:20)
不幸的是,答案是:这取决于。
最近的javascript引擎发生了重大转变,开始优化比以前更好。答案过去是:"局部变量存储在堆分配的堆栈帧中,以便闭包工作"。它不再那么简单了。
对于Scheme实现和闭包优化已经(或曾经是20 - 30年前)的研究(JavaScript继承了很多Scheme闭包,除了让它更加棘手的延续)。
我没有准备好纸质链接,但如果你没有非常高效的垃圾收集器,你也需要使用堆栈。棘手的部分是处理闭包,闭包需要堆分配的变量。因为使用了不同的策略。结果是混合:
这个领域在几个竞争引擎中变化非常快,所以答案可能仍然是"它取决于"
此外,在该语言的新版本中,我们将看到let
和const
等功能,这些功能实际上使引擎更容易优化分配决策。特别是不变性非常有用,因为你可以在堆栈中自由地复制值(例如,然后制作闭包对象的一部分),而不解决来自不同闭包的变化变量的冲突。