进一步扩展以前的答案...
从一般编译器的角度来看,忽略虚拟机特定的优化:
首先,我们进入词法分析阶段,在此阶段对代码进行标记化。
例如,可以产生以下令牌:
[]: ARRAY_INIT
[1]: ARRAY_INIT (NUMBER)
[1, foo]: ARRAY_INIT (NUMBER, IDENTIFIER)
new Array: NEW, IDENTIFIER
new Array(): NEW, IDENTIFIER, CALL
new Array(5): NEW, IDENTIFIER, CALL (NUMBER)
new Array(5,4): NEW, IDENTIFIER, CALL (NUMBER, NUMBER)
new Array(5, foo): NEW, IDENTIFIER, CALL (NUMBER, IDENTIFIER)
希望这应该为您提供足够的可视化,以便您可以了解需要多少(或更少)处理。
基于上述标记,我们知道ARRAY_INIT将始终产生一个数组。因此,我们只需创建一个数组并填充它。就模棱两可而言,词法分析阶段已经将ARRAY_INIT与对象属性访问器(例如obj[foo]
)或字符串/正则表达式文字中的括号(例如“ foo [] bar”或/ [] /)区分开来
这是微不足道的,但我们还有更多的令牌new Array
。此外,还不清楚我们是否只想创建一个数组。我们看到“新”令牌,但是“新”是什么?然后,我们看到IDENTIFIER令牌,它表示我们想要一个新的“数组”,但是JavaScript VM通常不会区分IDENTIFIER令牌和“本地全局对象”的令牌。因此...
每次遇到IDENTIFIER令牌时,我们都必须查找范围链。Javascript VM在每个执行上下文中都包含一个“ Activation对象”,其中可能包含“ arguments”对象,本地定义的变量等。如果在Activation对象中找不到它,我们将开始查找作用域链,直到达到全局作用域。如果未找到任何内容,则抛出ReferenceError
。
找到变量声明后,我们将调用构造函数。 new Array
是一个隐式函数调用,经验法则是函数调用在执行过程中较慢(因此,静态C / C ++编译器为何允许“函数内联”-像SpiderMonkey这样的JS JIT引擎必须即时进行)
该Array
构造函数重载。Array构造函数是作为本机代码实现的,因此它提供了一些性能增强功能,但仍需要检查参数长度并采取相应措施。此外,如果仅提供一个参数,则需要进一步检查参数的类型。new Array(“ foo”)产生[“ foo”],其中new Array(1)产生[undefined]
因此,为了简化这一切:通过数组文字,VM知道我们需要一个数组;使用时new Array
,VM需要使用额外的CPU周期来确定new Array
实际操作。