6

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

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

期望它比 while 循环慢23倍,因为本机方法必须循环两次,而 while 只循环一次,所以我在jsperf 上将其

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

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

是什么导致了如此大的差异?

4

4 回答 4

2

我预计这是由于两个主要因素:

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

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

于 2012-09-17T19:09:37.190 回答
0

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

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

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

于 2012-09-17T18:49:05.340 回答
0

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

于 2012-09-17T18:54:47.590 回答
0

我认为调用 map() 的作用与 while 循环的作用相同,而且每次迭代时都会调用一个函数。一般来说,函数调用非常慢。

于 2012-09-17T18:55:10.933 回答