javascript中的对象

alt*_*ter 14 javascript

原始值以javascript形式存储在堆栈中,但对象存储在堆中.我理解为什么要在堆栈中存储基元但是对象存储在堆中的任何原因?

T.J*_*der 24

实际上,在JavaScript中,甚至基元都存储在堆中而不是存储在堆栈中(但请参见下面的细分中的注释).当控件进入函数时,会创建该函数调用的执行上下文(对象),该函数具有变量对象.var函数的所有s和参数(加上其他一些东西)都是该匿名变量对象的属性,与命名对象的其他属性完全相同.使用了一个调用堆栈,但规范并不要求将堆栈用于"本地"变量存储,并且JavaScript的闭包将使得使用堆栈a'la C,C++等不切实际.规范中的详细信息.

相反,使用(链表).当您引用非限定符号时,解释器会检查变量对象以查找当前执行上下文,以查看它是否具有该名称的属性.如果是这样,它会被使用; 如果没有,则检查作用域链中的下一个变量对象(请注意,这是按词法顺序,而不是像调用堆栈那样的调用顺序),依此类推,直到达到全局执行上下文(全局执行上下文有一个变量对象就像任何其他执行上下文一样).全局EC的变量对象是我们可以在代码中直接访问的唯一对象:this在全局范围代码中指向它(以及在没有调用的任何函数中)this被明确设定).(在浏览器上,我们有另一种直接访问它的方法:全局变量对象有一个名为的属性window,用于指向自身.)

重新提出为什么对象存储在堆中的问题:因为它们可以彼此独立地创建和释放.C,C++和其他使用堆栈作为局部变量的人可以这样做,因为变量可以(并且应该)在函数返回时被销毁.堆栈是一种很好的有效方法.但是对象并没有以那种直截了当的方式被摧毁; 同时创建的三个对象可以具有完全不同的生命周期,因此堆栈对它们没有意义.而且由于JavaScript的本地存储在对象上,并且这些对象的生命周期(可能)与返回的函数无关......好吧,你明白了.:-)在JavaScript中,堆栈几乎只用于返回地址.


然而,值得注意的是,仅仅因为事情在概念上如上所述,这并不意味着引擎必须在引擎盖下这样做.只要它在外部工作如规范中所述,实现(引擎)可以自由地做他们喜欢的事情.据我所知,V8(谷歌的JavaScript引擎,用于Chrome和其他地方)做了一些非常聪明的事情,例如使用堆栈作为局部变量(甚至是函数中的本地对象分配),然后只将它们复制到堆中.必要的(例如,因为执行上下文或其上的单个对象在通话中存活).您可以看到,在大多数情况下,这将最大程度地减少堆碎片并且比依赖GC更积极有效地回收用于临时堆的内存,因为与大多数函数调用相关联的执行上下文不需要在调用中存活.我们来看一个例子:

function foo() {
    var n;

    n = someFunctionCall();
    return n * 2;
}

function bar() {
    var n;

    n = someFunction();
    setCallback(function() {
        if (n === 2) {
            doThis();
        }
        else {
            doThat();
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

在上面,像V8这样积极优化的引擎可以检测到调用的概念执行上下文foofoo返回时永远不需要存活.因此V8可以自由地在堆栈上分配该上下文,并使用基于堆栈的机制进行清理.

相反,为调用创建的执行上下文bar必须在bar返回后保持不变,因为有一个闭包(我们传入的匿名函数setCallback)依赖它.因此在编译时bar(因为V8会动态编译机器代码),V8可能会使用不同的策略,实际上在堆中分配上下文对象.

(如果上述任何一种曾使用eval任何方式,顺便说一句,这是可能的V8等引擎甚至没有尝试任何形式的优化,因为eval引入了太多的优化故障模式.另一个原因不能使用eval,如果你不"必须,你几乎从来没有.)

但这些是实施细节.从概念上讲,事情如上所述.

  • @TJ克罗德:我知道事情发生4年后,但我想说谢谢你这么棒的回答. (2认同)

Sha*_*fiz 6

对象的大小可以动态增长。因此,您需要调整它们的内存要求。这就是为什么它们存储在堆中。