在JavaScript中创建任意长度的零填充数组最有效的方法是什么?


当前回答

这里最快的是

(arr = []).length = len; arr.fill(0);

其他回答

这个concat版本在我的Chrome测试中要快得多(2013-03-21)。10000000个元素约200ms,而直接初始化为675。

function filledArray(len, value) {
    if (len <= 0) return [];
    var result = [value];
    while (result.length < len/2) {
        result = result.concat(result);
    }
    return result.concat(result.slice(0, len-result.length));
}

另外:如果你想用字符串填充数组,这是一种简洁的方法(虽然没有concat那么快):

function filledArrayString(len, value) {
    return new Array(len+1).join(value).split('');
}
function makeArrayOf(value, length) {
  var arr = [], i = length;
  while (i--) {
    arr[i] = value;
  }
  return arr;
}

makeArrayOf(0, 5); // [0, 0, 0, 0, 0]

makeArrayOf('x', 3); // ['x', 'x', 'x']

注意while通常比in、forEach等更有效。

截至ECMAScript2016,大型阵列有一个明确的选择。

由于这一答案在谷歌搜索中仍然排名靠前,所以这里有一个2017年的答案。

这里有一个当前的jsbench,其中有几十种流行的方法,包括迄今为止提出的许多方法。如果你找到更好的方法,请添加、分叉和分享。

我想指出,没有真正最有效的方法来创建任意长度的零填充数组。您可以优化速度,也可以优化清晰度和可维护性——根据项目的需要,两者都可以被视为更有效的选择。

在优化速度时,您需要:使用文字语法创建数组;设置长度,初始化迭代变量,并使用while循环遍历数组。这里有一个例子。

常量arr=[];arr.length=120000;设i=0;而(i<120000){arr[i]=0;i++;}

另一种可能的实施方式是:

(arr = []).length = n;
let i = 0;
while (i < n) {
    arr[i] = 0;
    i++;
}

但我强烈反对在实践中使用第二次植入,因为它不太清楚,也不允许在数组变量上保持块范围。

这比用for循环填充要快得多,比标准方法快90%左右

const arr = Array(n).fill(0);

但这种填充方法由于其清晰、简洁和可维护性,对于较小的阵列来说仍然是最有效的选择。除非您制作了大量长度为数千或更多的阵列,否则性能差异可能不会让您丧命。

其他一些重要的注意事项。大多数风格指南都建议您在使用ES6或更高版本时,如果没有非常特殊的原因,不要再使用var。对于不会被重新定义的变量使用const,对于会被重新定义变量使用let。MDN和Airbnb的风格指南是获取更多最佳实践信息的好地方。这些问题并不涉及语法,但重要的是,在搜索大量新旧答案时,熟悉JS的人必须了解这些新标准。

我知道我在某个地方有这个原型:)

Array.prototype.init = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this[n] = x; }
    return this;
}

var a = (new Array(5)).init(0);

var b = [].init(0,4);

编辑:测试

作为对Joshua和其他方法的回应,我运行了自己的基准测试,我看到了与报告完全不同的结果。

以下是我测试的内容:

//my original method
Array.prototype.init = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this[n] = x; }
    return this;
}

//now using push which I had previously thought to be slower than direct assignment
Array.prototype.init2 = function(x,n)
{
    if(typeof(n)=='undefined') { n = this.length; }
    while (n--) { this.push(x); }
    return this;
}

//joshua's method
function newFilledArray(len, val) {
    var a = [];
    while(len--){
        a.push(val);
    }
    return a;
}

//test m1 and m2 with short arrays many times 10K * 10

var a = new Date();
for(var i=0; i<10000; i++)
{
    var t1 = [].init(0,10);
}
var A = new Date();

var b = new Date();
for(var i=0; i<10000; i++)
{
    var t2 = [].init2(0,10);
}
var B = new Date();

//test m1 and m2 with long array created once 100K

var c = new Date();
var t3 = [].init(0,100000);
var C = new Date();

var d = new Date();
var t4 = [].init2(0,100000);
var D = new Date();

//test m3 with short array many times 10K * 10

var e = new Date();
for(var i=0; i<10000; i++)
{
    var t5 = newFilledArray(10,0);
}
var E = new Date();

//test m3 with long array created once 100K

var f = new Date();
var t6 = newFilledArray(100000, 0)
var F = new Date();

结果:

IE7 deltas:
dA=156
dB=359
dC=125
dD=375
dE=468
dF=412

FF3.5 deltas:
dA=6
dB=13
dC=63
dD=8
dE=12
dF=8

因此,据我估计,总体而言,推送速度确实较慢,但在FF中使用较长的阵列时表现更好,但在IE中表现更差,这只是总体上很糟糕(quel surprise)。

循环代码最短

a=i=[];for(;i<100;)a[i++]=0;

edit:
for(a=i=[];i<100;)a[i++]=0;
or
for(a=[],i=100;i--;)a[i]=0;

安全var版本

var a=[],i=0;for(;i<100;)a[i++]=0;

edit:
for(var i=100,a=[];i--;)a[i]=0;