为什么使用while循环更快地填充新数组?

时间:2012-09-17 18:38:48

标签: javascript performance

我正在考虑使用本机方法创建包含默认值的数组的方法,最后以

结束
function pushMap(length, fill){
    var a = [], b = [];
    a.length = length;
    b.push.apply(b,a);
    return b.map(function(){return fill;});
}

期望它比while循环慢 2 3 倍,因为本机方法必须循环两次,而while循环只有一次,所以我比较了jsperf反对

function whileLengthNew(len, val) {
    var rv = new Array(len);
    while (--len >= 0) {
        rv[len] = val;
    }
    return rv;
}

实际上 18 27 慢(在Ubuntu上使用Google Chrome进行测试,欢迎使用浏览器/操作系统)。

会发生什么导致如此大的差异?

4 个答案:

答案 0 :(得分:2)

我希望这是由于两个主要因素:

  1. 内存分配 - whileLengthNew首先创建一个正确大小的数组,然后对其进行操作,pushMap使用map一次创建一个元素的最终数组。这可能会导致多次分配,尤其是在源阵列很大的情况下。 (创建初始ab数组的方式基本上是无关紧要的,因为map正在构建一个新的数组以便返回 - 它实际上并没有改变{ {1}})

  2. 函数调用开销 - 在调用b时,您正在为数组的每个元素调用一个函数。这涉及相当多的开销;设置激活记录和作用域链,堆栈操作以及返回返回值。 - 所有这些都可以访问函数中常量的变量。最重要的是,您已经设置了一个闭包,所以即使访问map变量也比fill版本慢。

答案 1 :(得分:0)

我不是javascript专家,我没有贡献任何javascript引擎,所以以下只是一个猜测:

pushMap功能中,您有很多事情要发生  1.首先,你以一种可能非常低效的方式将var a扩展到你想要的大小。 length只是数组中的一个属性,因此底层实现具有更改属性时的回调,或者底层实现可以处理更改的长度属性,并在下次访问其中的某些内容时使用它。
 2.创建var b似乎效率更低,因为您正在调用反射类型方法apply以使b成为特定长度的数组。
 3.然后你基本上以函数方式调用foreach循环,这可能比while循环慢一点,因为内部函数的开销(闭包我猜是因为它是JS)

如果您使用与创建var b相同的方式创建var rv,则可能会获得更均匀的结果。希望这可以帮助。 EDIT 当然在这种情况下不起作用,因为map只适用于数组的初始化值。这意味着这种方法较慢的另一个原因(并且OP在他的问题中提到了这一点)你正在初始化地图两次,一次是空值,再一次是你想要的值。

答案 2 :(得分:0)

你为每一个值进行不必要的函数调用,因此JavaScript虚拟机很难优化这个模式,你的数组也不会被内部视为一个简单的数组,而是复杂的数据结构,另一方面你在while循环中使用非常简单的数组访问模式,我怀疑你是否会使用带有rv.length的for循环作为停止条件它仍然会更快

答案 3 :(得分:0)

我认为调用map()与while循环的作用相同,此外还在每次迭代时调用函数。一般来说,函数调用非常慢。