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

IT技术 javascript performance
2021-01-29 03:11:26

我运行了这段代码并得到了以下结果。我很想知道为什么[]更快?

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')
  • 使用[]:299ms
  • 使用new:363ms

感谢Raynos,这里是这段代码基准测试和一些更可能的定义变量的方法。

在此处输入图片说明

5个回答

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

从一般编译器的角度来看,不考虑 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)

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

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

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

  3. 每次遇到 IDENTIFIER 令牌时,我们都必须查找作用域链。Javascript VM 为每个执行上下文包含一个“激活对象”,其中可能包含“参数”对象、本地定义的变量等。如果我们在 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 实际执行的操作。

刚刚做了一个测试用例。如果您提前知道数组的大小,则 new Array(n) 会更快jsperf.com/square-braces-vs-new-array
2021-03-12 03:11:26
不是 a = new Array(1000);for(from 0 to 999){a[i]=i} 比 a = [];for(from 0 to 999){a[i]=i} 快,因为分配开销呢?
2021-03-30 03:11:26

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

我认为这是有道理的。在 IIFE 中运行 OP 的循环测试会对性能产生(相对)实质性的影响。包括var Array = window.Array提高了new Array测试的性能
2021-03-18 03:11:26
我认为这是不对的,因为这个 console.time('more vars new'); for(var i=0; i<200000; i++){ var arr = new Array() }; console.timeEnd('更多变量新'); more vars new: 390ms 和这个 console.time('more vars new'); var myOtherObject = {}, myOtherArray = []; for(var i=0; i<200000; i++){ var arr = new Array() }; console.timeEnd('更多变量新'); 更多 vars new: 369ms 同时返回
2021-03-19 03:11:26
Array除了一个参数len和多个参数。其中 as[]只接受多个参数。Firefox 测试也显示几乎没有区别。
2021-03-25 03:11:26
检查参数也可能有所贡献。
2021-03-27 03:11:26

好问题。第一个示例称为数组文字。它是许多开发人员创建数组的首选方式。性能差异可能是由于检查 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 中,这两个变体几乎相等。

这会有些道理

对象字面量使我们能够编写支持许多功能的代码,但仍然使我们的代码实现者相对简单。无需直接调用构造函数或维护传递给函数的参数的正确顺序等。

http://www.dyn-web.com/tutorials/obj_lit.php