在Javascript中如何分配变量内存?

我想知道本地变量如何在javascript中分配内存。 在C和C ++中,局部变量存储在堆栈中。 在JavaScript中它是一样的吗? 或者一切都存储在堆?


这实际上是一个非常有趣的Javascript领域。 规范中的细节,但是:Javascript处理局部变量的方式与C的方式完全不同。 当你调用函数时,除了别的以外,还会创建一个用于该调用的“变量环境”,其中有一些称为“绑定对象”的东西。 (简称为“变量对象”;称为“变量环境的绑定对象”只是有点冗长!)变量对象具有函数参数的属性,函数中声明的所有局部变量,以及在函数中声明的所有函数(以及其他一些事情)。 首先检查函数中的非限定引用(例如, 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 ,会用这些属性创建一个变量对象:

  • ab - 函数的参数
  • c - 函数中声明的局部变量
  • bar - 在函数中声明的函数
  • (...和其他一些东西)
  • foo执行语句c = a + b; ,它引用foo调用的变量对象上的cab属性。 当foo返回对其内部声明的bar函数的引用时, bar保持foo返回的调用。 由于bar对该特定的foo调用的变量对象具有(隐藏的)引用,所以变量对象仍然存在(而在正常情况下,它没有未完成的引用,因此可用于垃圾收集)。

    之后,当我们调用bar ,为该调用创建一个新的变量对象,其中包含一个名为d的属性 - 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                      |
    +----------------------------+

    实现可以自由地使用他们想要的任何机制来实现上述目标。 对于函数调用来说,直接访问变量对象是不可能的,而且规范清楚地表明,如果变量对象只是一个概念,而不是实现的文字部分,那就完全没问题。 一个简单的实现可能只是字面上做规范说什么; 一个更复杂的可以在没有包含闭包(为了速度利益)时使用堆栈,或者可以总是使用堆栈,但是当弹出堆栈时“关闭”关闭所需的变量对象。 在任何特定情况下知道的唯一方法是查看他们的代码。 :-)

    关于闭包,范围链等的更多信息请见:

  • 闭包不复杂
  • 糟糕的误解'var'

  • 不幸的是答案是:这取决于。

    最近的javascript引擎发生了很大的转变,开始比以前更好的优化。 过去的答案是:“局部变量存储在堆分配堆栈帧中,以便闭包工作”。 它不再那么简单了。

    曾经(或曾经是20-30年前)研究Scheme实现和闭包优化(JavaScript继承了相当多的Scheme闭包,除了使其更加棘手的连续性之外)。

    我没有准备好纸张链接,但是如果你没有非常高效的垃圾收集器,你也需要使用堆栈。 然后棘手的部分是处理闭包,这需要变量堆分配。 为此使用不同的策略。 其结果是一个混合体,其中:

  • 通过内联函数,可以显着减少分配/释放堆分配帧的数量
  • 一些变量可以安全地放在堆栈上,因为它的时间跨度是有限的(它通常也连接到内联函数调用)
  • 在某些情况下,您知道您可能正在创建闭包,但您可以等到这种情况发生,然后为其分配堆栈帧并从堆栈中复制当前值
  • 有优化连接到尾部调用,在那里你可以堆分配更早,然后重新使用堆栈框架的下一个函数调用,但是这不是在JavaScript引擎中使用据我所知
  • 这个领域在几个竞争引擎中变化非常快,所以答案可能仍然是“取决于”

    另外,在新版本的语言中,我们将看到像letconst这样的功能,这些功能实际上使引擎更容易优化分配决策。 特别是不变性非常有用,因为您可以自由地将值从堆栈中复制出来(并且例如使其成为闭包对象的一部分),而不必解决来自不同闭包的变化变量的冲突。

    链接地址: http://www.djcxy.com/p/82523.html

    上一篇: How variables are allocated memory in Javascript?

    下一篇: Is this explicit heap or stack dynamic?