为什么arr = []比arr = new Array要快?


146

我运行了这段代码,并得到以下结果。我很好奇为什么[]会更快?

console.time('using[]')
for(var i=0; i<200000; i++){var arr = []};
console.timeEnd('using[]')

console.time('using new')
for(var i=0; i<200000; i++){var arr = new Array};
console.timeEnd('using new')
  • 使用[]:299毫秒
  • 使用new:363ms

感谢Raynos,这里是此代码的基准,以及定义变量的更多可能方式。

在此处输入图片说明


5
您可能对jsperf感兴趣。
Pointy


注意关键字new。这意味着“请降低效率”。它永远都没有意义,并且要求浏览器执行常规实例化而不是尝试进行优化。
beatgammit 2011年

2
@kinakuta号 它们都创建新的非相等对象。我的意思[]是等效new Array()于源代码,而不是从表达式返回的对象
Raynos 2011年

1
是的,这不是很重要。但我想知道。
Mohsen

Answers:


195

进一步扩展以前的答案...

从一般编译器的角度来看,忽略虚拟机特定的优化:

首先,我们进入词法分析阶段,在此阶段对代码进行标记化。

例如,可以产生以下令牌:

[]: 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)

希望这应该为您提供足够的可视化,以便您可以了解需要多少(或更少)处理。

  1. 基于上述标记,我们知道ARRAY_INIT将始终产生一个数组。因此,我们只需创建一个数组并填充它。就模棱两可而言,词法分析阶段已经将ARRAY_INIT与对象属性访问器(例如obj[foo])或字符串/正则表达式文字中的括号(例如“ foo [] bar”或/ [] /)区分开来

  2. 这是微不足道的,但我们还有更多的令牌new Array。此外,还不清楚我们是否只想创建一个数组。我们看到“新”令牌,但是“新”是什么?然后,我们看到IDENTIFIER令牌,它表示我们想要一个新的“数组”,但是JavaScript VM通常不会区分IDENTIFIER令牌和“本地全局对象”的令牌。因此...

  3. 每次遇到IDENTIFIER令牌时,我们都必须查找范围链。Javascript VM在每个执行上下文中都包含一个“ Activation对象”,其中可能包含“ arguments”对象,本地定义的变量等。如果在Activation对象中找不到它,我们将开始查找作用域链,直到达到全局作用域。如果未找到任何内容,则抛出ReferenceError

  4. 找到变量声明后,我们将调用构造函数。 new Array是一个隐式函数调用,经验法则是函数调用在执行过程中较慢(因此,静态C / C ++编译器为何允许“函数内联”-像SpiderMonkey这样的JS JIT引擎必须即时进行)

  5. Array构造函数重载。Array构造函数是作为本机代码实现的,因此它提供了一些性能增强功能,但仍需要检查参数长度并采取相应措施。此外,如果仅提供一个参数,则需要进一步检查参数的类型。new Array(“ foo”)产生[“ foo”],其中new Array(1)产生[undefined]

因此,为了简化这一切:通过数组文字,VM知道我们需要一个数组;使用时new Array,VM需要使用额外的CPU周期来确定new Array 实际操作。


不是a = new Array(1000); for(从0到999){a [i] = i}比a = []; for(从0到999){a [i] = i}快分配开销呢?
Y. Yoshii,

刚做了一个测试用例。如果您提前知道数组的大小,则new Array(n)会更快。jsperf.com/square
Y. Yoshii

27

一个可能的原因是new Array需要进行名称查找Array(您可以在范围内使用该名称的变量),[]而不需要。


4
检查参数也可能有所帮助。
Leonid

Array除了一个参数len和多个参数。其中,as []仅接受多个参数。Firefox测试也显示几乎没有区别。
雷诺斯2011年

我认为这有些道理。在IIFE中运行OP的环路测试会对性能产生(相对)重大影响。包括var Array = window.Array提高new Array测试性能。
user113716 2011年

我认为这是不对的,因为此console.time('more vars new'); for(var i = 0; i <200000; i ++){var arr = new Array()}; console.timeEnd('more vars new'); more vars new:390ms and this console.time('more vars new'); var myOtherObject = {},myOtherArray = []; for(var i = 0; i <200000; i ++){var arr = new Array()}; console.timeEnd('more vars new'); 更多新的变量:369ms返回相同的时间
Mohsen

2

好问题。第一个示例称为数组文字。在许多开发人员中,这是创建数组的首选方式。性能差异可能是由于检查新Array()调用的参数然后创建对象而文字直接创建数组而引起的。

我认为,性能上的相对较小差异支持了这一点。顺便说一下,您可以使用对象和对象文字{}进行相同的测试。



1

同样,有趣的是,如果预先知道数组长度(元素将在创建后立即添加),则在最新的Google Chrome 70+上使用具有指定长度数组构造函数快得多

  • new Array( %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中,这两个变体几乎相等。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.