为什么 arr = [] 比 arr = new Array 快?
进一步扩展以前的答案......
从一般编译器的角度来看,不考虑 VM 特定的优化:
首先,我们经历词法分析阶段,在此阶段对代码进行标记。
例如,可以产生以下令牌:
[]: 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
. 此外,目前还不完全清楚我们只是想创建一个数组。我们看到了“new”令牌,但是“new”是什么?然后我们会看到 IDENTIFIER 令牌,它表示我们想要一个新的“数组”,但 JavaScript VM 通常不区分 IDENTIFIER 令牌和“本地全局对象”的令牌。所以...每次遇到 IDENTIFIER 令牌时,我们都必须查找作用域链。Javascript VM 为每个执行上下文包含一个“激活对象”,其中可能包含“参数”对象、本地定义的变量等。如果我们在 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
实际执行的操作。
一个可能的原因是new Array
需要名称查找Array
(您可以在范围内使用具有该名称的变量),[]
而不需要。
好问题。第一个示例称为数组文字。它是许多开发人员创建数组的首选方式。性能差异可能是由于检查 new Array() 调用的参数然后创建对象而导致的,而文字直接创建了一个数组。
我认为性能上相对较小的差异支持了这一点。顺便说一下,您可以对 Object 和对象文字 {} 进行相同的测试。
此外,有趣的是,如果预先知道数组的长度(元素将在创建后立即添加),则在最近的 Google Chrome 70+ 上使用具有指定长度的数组构造函数要快得多。
“新数组( %ARR_LENGTH% ) ”——100% (更快)!
“ [] ” – 160-170% (较慢)
测试可以在这里找到 - https://jsperf.com/small-arr-init-with-known-length-brackets-vs-new-array/2
注意:此结果在Google Chrome v.70+上测试;在Firefox v.70和 IE 中,这两个变体几乎相等。
这会有些道理
对象字面量使我们能够编写支持许多功能的代码,但仍然使我们的代码实现者相对简单。无需直接调用构造函数或维护传递给函数的参数的正确顺序等。