为什么嵌套一堆块会导致JavaScript中的堆栈溢出

为什么嵌套一堆块会导致JavaScript中的堆栈溢出,javascript,stack-overflow,Javascript,Stack Overflow,代码{}在JavaScript中是完全合法的,因为它代表一个 然而,我注意到在另一个Chrome*中嵌套大量块({{…}}): 未捕获范围错误:超过最大调用堆栈大小 为什么这里会发生堆栈溢出 下面是一个例子来说明这个问题(JSFIDLE崩溃) 在JSRoom中询问时发现,在chrome和Firefox上,魔法数字分别为3913和2555 什么被推到堆栈中?为什么? (*)我已经检查过了,IE和Firefox中也出现了这种情况 更新:我已经检查了IE是否能够避免堆栈溢出异常。它投掷了两次,但第

代码
{}
在JavaScript中是完全合法的,因为它代表一个

然而,我注意到在另一个Chrome*中嵌套大量块(
{{…}}
):

未捕获范围错误:超过最大调用堆栈大小

为什么这里会发生堆栈溢出


下面是一个例子来说明这个问题(JSFIDLE崩溃)

在JSRoom中询问时发现,在chrome和Firefox上,魔法数字分别为3913和2555

什么被推到堆栈中?为什么?


(*)我已经检查过了,IE和Firefox中也出现了这种情况

更新:我已经检查了IE是否能够避免堆栈溢出异常。它投掷了两次,但第三次没有。如果任何读者都有IE,并且愿意测试它的旧版本(如IE8和IE9),并让我知道发生了什么,我将非常感激。

的默认实现虽然简单而优雅,但只使用一种方法解析每种语言的语法规则。这些方法递归调用其他方法,因此当嵌套规则过多时,它会超出堆栈大小。Chrome和Firefox都使用这种解释器实现

您会注意到,许多“+”虽然与范围无关,但会导致相同的异常:

+ + + + + + + + + ... // same error

首先,戈德是完全正确的。这是由解析器的递归性质造成的,所以请给他投票表决。但证据需要证明,OP希望我将此作为一个单独的答案发布

火狐 那么,到哪里去了解它是如何完成的呢?问一些正在制造引擎的人。所以我转到了
上的
#jsapi
频道irc://irc.mozilla.org
并询问他们:

zirak:well,使用递归下降解析器,所有的结果将大致对应于C堆栈上的一个帧

zirak:解析器位于js/src/front/parser.cpp

zirak:Parser::statement(bool-canHaveDirectives)和Parser::statements()差不多

zirak:在这种情况下,递归将是Parser::blockStatement->Parser::statements->Parser::statement->Parser::blockStatement->Parser::statement->Parser::blockStatement

这几乎就是答案。前往mozilla中央存储库并深入挖掘,我们有了嫌疑犯:

因此,我们得到的是:

  • statements
    调用
    blockStatement
    ,解析块以查找另一个块,调用
    • statements
      调用
      blockStatement
      ,解析块以查找另一个块,调用
      • statements
        调用
        blockStatement
        ,解析块以查找另一个块,调用
直到堆栈崩溃,我猜

所以我们有Firefox的源代码

Chrome/Chrome/基于v8的任何其他产品 从Firefox中吸取教训,我转到v8项目,查找名为
parser
的文件。果然,它就在那儿

下一件事是在解析块时查找,因此我天真地搜索了
语句
,找到了有希望的结果

今天是我们的幸运日,一个巨大的开关!这就是我们所关心的,一个调用
ParseBlock
,另一个有前途的名字

实际上,to
ParseStatement
。因此,明确地说,我们有两个功能:

就像我们在Firefox中看到的那样,他们互相打电话:

  • ParseStatement
    调用
    ParseBlock
    ,该语句解析块,以查找另一个块,调用
    • ParseStatement
      调用
      ParseBlock
      ,该语句解析块,以查找另一个块,调用
      • ParseStatement
        调用
        ParseBlock
        ,该语句解析块,以查找另一个块,调用
直到卡布姆离开堆栈

游猎 (很抱歉在上次编辑中称之为封闭源代码!)Safari的js引擎是,它位于WebKit项目中。查找函数与查找Chrome的函数几乎相同,因此让我们跳到有趣的部分:

我们在中间有一个额外的功能,但原理是一样的:

  • parseSourceElements
    它调用
    parseStatement
    它调用
    parseBlockStatement
    ,它解析块,找到另一个块,调用
    • parseSourceElements
      它调用
      parseStatement
      它调用
      parseBlockStatement
      ,它解析块,找到另一个块,调用
      • parseSourceElements
        它调用
        parseStatement
        它调用
        parseBlockStatement
        ,它解析块,找到另一个块,调用
繁荣

IE(以及所有其他封闭源代码,如Opera) …将仍然是一个谜,除非他们突然感到需要开源,或者如果一位有进取心的员工与我们分享了内部信息。上面两个伟大的引擎是以同样的方式实现的,因此我们可以假设其他浏览器也是这样做的


如果浏览器没有崩溃,这是一个有趣的问题,但这个答案不可能给出答案。

@faffafff为什么会创建堆栈帧,因为它不是一个可能的作用域?@faffaff Javascript没有块作用域。@Paulpro这几乎是正确的。JavaScript确实有块作用域,但仅在
try/catch
with
中。如果将足够多的表达式传递给逗号运算符,则还会出现堆栈溢出错误(请参阅)。有事告诉我们