如何在JavaScript中初始化数组的长度?


541

我阅读过的有关JavaScript数组的大多数教程(包括w3schoolsdevguru)都建议您可以通过使用var test = new Array(4);语法将整数传递给Array构造函数来初始化具有一定长度的数组。

在我的js文件中广泛使用此语法后,我通过jsLint运行了其中一个文件,结果很糟糕

错误:第1行第22个字符处出现问题:预期为“)”,而是看到了“ 4”。
var test = new Array(4);
第1行第23个字元的问题:预期为';' 而是看到')'。
var test = new Array(4);
第1行第23个字符处的问题:需要一个标识符,而看到了')'。

阅读完jsLint对它的行为的解释之后,看起来jsLint并不真正喜欢new Array()语法,而是[]在声明数组时更喜欢。

所以我有几个问题:

首先,为什么?我使用new Array()语法代替会冒任何风险吗?我应该注意浏览器的不兼容性吗?

其次,如果我切换到方括号语法,是否有任何方法可以声明一个数组并将其长度全部设置在一行上,或者我必须执行以下操作:

var test = [];
test.length = 4;

标准js也建议 不要使用new Array()一般方法,但是可以指定大小。我认为一切都取决于整个上下文中的代码一致性。
cregox

对于那些希望预分配更严格的数组结构的用户,可以使用Typed Arrays。请注意,性能优势可能会有所不同
rovyko '19

Answers:


399
  1. 为什么要初始化长度?从理论上讲,这是没有必要的。它甚至可能导致混乱的行为,因为所有使用length来查明数组是否为空的测试都会报告该数组不为空。
    一些 试验表明,设定大的阵列的初始长度可以如果该阵列被充满之后更有效,但性能增益(如果有的话)似乎不同于浏览器的浏览器。

  2. jsLint不喜欢,new Array()因为构造方法不明确。

    new Array(4);

    创建一个长度为 4 的空数组但是

    new Array('4');

    创建一个包含value 的数组'4'

关于您的评论:在JS中,您不需要初始化数组的长度。它动态增长。您可以将长度存储在一些变量中,例如

var data = [];
var length = 5; // user defined length

for(var i = 0; i < length; i++) {
    data.push(createSomeObject());
}

13
数组中对象的数量是用户定义的,因此我让用户选择一个数字,然后使用那么多插槽初始化数组。然后,我运行一个for循环,遍历数组的长度并填充它。我猜想还有其他方法可以在JavaScript中执行此操作,因此真正的答案是“为什么要执行此操作?” 是“由于使用其他语言编程时形成的旧习惯”。:)
Michael Martin-Smucker

4
@mlms只需让for循环遍历用户定义的数字即可,而不必设置数组长度然后对其进行遍历。这对您来说更有意义吗?
森那维达斯

149
<blockquote>为什么要初始化长度?从理论上讲,这是没有必要的。并且所有使用该长度来确定数组是否为空的测试都将失败。</ blockquote>嗯,也许是性能?设置数组中预先存在的元素比动态添加元素要快。
代码头

43
确实没有必要“放弃旧的编程习惯的时间”。正确结构化的语言将永远胜过ecmascript mombo jambo
user151496 2013年

10
@codehead:对此报价作出回应:“设置数组的现有元素比动态添加数组要快。”:请注意,new Array(10)不会创建包含10个未定义元素的数组。它只是简单地创建具有10长度的空数组看到这个答案对于讨厌的真相:stackoverflow.com/questions/18947892/...
Milimetric

596
  • Array(5) 给您一个长度为5但没有值的数组,因此您无法对其进行迭代。

  • Array.apply(null, Array(5)).map(function () {}) 给您一个长度为5且未定义为值的数组,现在可以对其进行迭代。

  • Array.apply(null, Array(5)).map(function (x, i) { return i; }) 给您一个长度为5且值为0、1、2、3、4的数组。

  • Array(5).forEach(alert)什么都不做,Array.apply(null, Array(5)).forEach(alert)会给您5条警报

  • ES6给我们,Array.from所以现在您也可以使用Array.from(Array(5)).forEach(alert)

  • 如果您想使用某个值进行初始化,那么最好知道这些...
    Array.from('abcde')Array.from('x'.repeat(5))
    或者Array.from({length: 5}, (v, i) => i) // gives [0, 1, 2, 3, 4]


11
这就是我想要的。我想按逻辑顺序应用地图;这应该做。谢谢!
jedd.ahyoung,2015年

3
为什么Array.apply()给它未定义的值?
wdanxna 2015年

5
@wdanxna当Array被赋予多个参数时,它将遍历该arguments对象并将每个值显式应用到新数组。当您Array.apply使用数组或具有length属性的对象进行调用时,Array将使用length来显式设置新数组的每个值。这就是为什么Array(5)给出5个省略数组,而Array.apply(null, Array(5))给出5个未定义数组的原因。欲了解更多信息,请参阅答案。
KylePlusPlus 2015年

2
Array(5)和新Array(5)有什么区别?
Petr Hurtak

2
Array.apply(null, Array(5)) 也可以写成Array.apply(null, {length: 5})。确实并没有太大的区别,但是后者明确表示其意图是创建一个数组length 5,而不是包含一个数组5
AlexMorley-Finch

271

借助ES2015,.fill()您现在可以轻松执行以下操作:

// `n` is the size you want to initialize your array
// `0` is what the array will be filled with (can be any other value)
Array(n).fill(0)

比这简明得多 Array.apply(0, new Array(n)).map(i => value)

可以插入0in .fill()并在不使用参数的情况下运行,这将用填充数组undefined。(但是,这将在Typescript中失败


1
@AralRoca您始终可以使用MDN页面上提供的Polyfill,或考虑使用Modernizr
AP。

4
是的,请尝试一下你的评论前
AP。

1
@GarretWilson和@Christian 在规范中When Array is called as a function rather than as a constructor, it also creates and initializes a new Array object. Thus the function call Array(…) is equivalent to the object creation expression new Array(…) with the same arguments
AP。

1
@AP。,零是多余的。做Array(n).fill()
和平者'18

2
@Pacerier我不认为0是多余的。根据es6类型定义,这是函数签名:fill(value:T,start ?: number,end ?: number):this; 因此,填充值似乎不是可选的。由于上面编写了函数调用,因此Typescript编译将失败。
Micha Schwab

148
[...Array(6)].map(x => 0);
// [0, 0, 0, 0, 0, 0]

要么

Array(6).fill(0);
// [0, 0, 0, 0, 0, 0]

注意:您不能循环空插槽,即 Array(4).forEach(() => …)


要么

打字稿安全

Array(6).fill(null).map((_, i) => i);
// [0, 1, 2, 3, 4, 5]

要么

使用函数的经典方法(可在任何浏览器中使用)

function NewArray(size) {
    var x = [];
    for (var i = 0; i < size; ++i) {
        x[i] = i;
        return x;
    }
}

var a = NewArray(10);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

创建嵌套数组

当直观地创建2D数组时,fill应创建新实例。但是实际上将要发生的是将同一数组存储为参考。

var a = Array(3).fill([6]);
// [  [6], [6], [6]  ]

a[0].push(9);
// [  [6, 9], [6, 9], [6, 9]  ]

var a = [...Array(3)].map(x => []);

a[0].push(4, 2);
// [  [4, 2], [], []  ]

因此,一个3x2阵列将如下所示:

[...Array(3)].map(x => Array(2).fill(0));
// [  [0, 0], [0, 0], [0, 0]  ]

N维数组

function NArray(...dimensions) {
    var index = 0;
    function NArrayRec(dims) {
        var first = dims[0], next = dims.slice().splice(1); 
        if(dims.length > 1) 
            return Array(dims[0]).fill(null).map((x, i) => NArrayRec(next ));
        return Array(dims[0]).fill(null).map((x, i) => (index++));
    }
    return NArrayRec(dimensions);
}

var arr = NArray(3, 2, 4);
// [   [  [ 0,  1,  2,  3 ] , [  4,  5,  6,  7]  ],
//     [  [ 8,  9,  10, 11] , [ 12, 13, 14, 15]  ],
//     [  [ 16, 17, 18, 19] , [ 20, 21, 22, 23]  ]   ]

初始化棋盘

var Chessboard = [...Array(8)].map((x, j) => {
    return Array(8).fill(null).map((y, i) => {
        return `${String.fromCharCode(65 + i)}${8 - j}`;
    });
});

// [ [A8, B8, C8, D8, E8, F8, G8, H8],
//   [A7, B7, C7, D7, E7, F7, G7, H7],
//   [A6, B6, C6, D6, E6, F6, G6, H6],
//   [A5, B5, C5, D5, E5, F5, G5, H5],
//   [A4, B4, C4, D4, E4, F4, G4, H4],
//   [A3, B3, C3, D3, E3, F3, G3, H3],
//   [A2, B2, C2, D2, E2, F2, G2, H2],
//   [A1, B1, C1, D1, E1, F1, G1, H1] ]

1
a[0].push(9); // [ [6, 9], [6], [6] ] 应该是 a[0].push(9); // [ [6, 9], [6, 9], [6, 9] ] 因为每个嵌套数组都存储为引用,所以更改一个数组会影响其余数组。尽管我认为您可能只是输错了:)
Alex_Zhong

@Alex_Zhong tru,谢谢,在编辑中丢失了它
Vlad

68

最短的:

[...Array(1000)]

10
建议,但请注意,绝对不要在JS中以a开头[而不使用a ;的行,因为它将尝试取消引用上面的行。因此,可预测地在代码的任何部分中使用此行的安全方法是:;[...Array(1000)]//.whateverOpsNeeded()
AP。

并不是的。试用开发人员工具。 [...Array(5)].map((item, index) => ({ index })) 也尝试一下:[...Array(5)]; console.log('hello');
迈克尔·马莫里蒂

2
尝试在多行js文件中使用它。如果您的第一行是const a = 'a'和下一行[...Array(5)].//irrelevent。您认为第一行会解决什么?这将const a = 'a'[...Array(5)]导致:Uncaught SyntaxError: Unexpected token ...
AP。

7
是的,但是在这种情况下,我会说问题出在其他地方,如今分号和棉绒很重要。
Michael Mammoliti

8
@AP这个例子正是许多样式指南要求用分号结尾每个语句的原因。const a = 'a'在这种情况下将是Lint错误。无论如何,它确实与这个答案无关。
graup

41

ES6引入了Array.from它,您可以Array从任何“类似数组”iterables对象创建一个:

Array.from({length: 10}, (x, i) => i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

在这种情况下,{length: 10}代表了“数组状”对象的最小定义:一个仅length定义了属性的空对象。

Array.from 允许第二个参数映射到结果数组上。


2
@dain我认为[... Array(10)]更好stackoverflow.com/a/47929322/4137472
Alexander Shutau,

1
为什么更好?不鼓励使用Array构造函数,某些
短毛猫

26

这会将length属性初始化为4:

var x = [,,,,];

3
那很聪明。它没有构造函数的灵活性,因为您不能使用变量来设置长度,但是它确实回答了我最初提出的问题,即+1。
Michael Martin-Smucker

12
想象一下,当性能真的很重要时,不要重复300个项目!
Marco Luglio 2012年

9
当预分配如此小的大小的数组时,可能不会有多少性能提升。创建更大的阵列时,性能差异会更好。在这种情况下,用逗号初始化它们会有点不堪重负。
Marco Luglio 2012年

6
我认为这会产生,因为最后一个逗号的不同的浏览器不同的结果,有时是4,有时5,看stackoverflow.com/questions/7246618/...
jperelli

1
怎么var size = 42; var myArray = eval("["+",".repeat(size)+"]");样 (不那么严重;)
xoxox

15

令我惊讶的是,还没有一个功能性的解决方案可以让您在一行中设置长度。以下基于UnderscoreJS:

var test = _.map(_.range(4), function () { return undefined; });
console.log(test.length);

由于上述原因,除非我想将数组初始化为特定值,否则我将避免这样做。有趣的是,还有其他实现范围的库,包括Lo-dash和Lazy,它们可能具有不同的性能特征。


这里确实不需要下划线的黑魔法。依赖性就像糖一样,起初看起来很甜,但是在您不知道它之前就患有糖尿病。
罗曼·文森特

9

请人们暂时不要放弃您的旧习惯。在分配内存一次然后使用该数组中的条目(如旧),以及随着数组的增长多次分配内存之间,速度存在很大差异(这不可避免地是系统采用其他建议的方法在后台进行的操作) 。

当然,这一切都无关紧要,除非您想对更大的阵列做一些很棒的事情。然后就可以了。

鉴于目前在JS中似乎仍然无法设置数组的初始容量,我使用以下方法...

var newArrayWithSize = function(size) {
  this.standard = this.standard||[];
  for (var add = size-this.standard.length; add>0; add--) {
   this.standard.push(undefined);// or whatever
  }
  return this.standard.slice(0,size);
}

需要权衡:

  • 第一次调用该方法所需的时间与其他方法所需的时间相同,但以后的调用将花费很少的时间(除非要求使用更大的数组)。
  • standard阵列确实会永久保留您所需的最大阵列的空间。

但是,如果它适合您的工作,那么可能会有所收获。非正式时机

for (var n=10000;n>0;n--) {var b = newArrayWithSize(10000);b[0]=0;}

以相当快的速度(对于10000,大约需要50ms,因为n = 1000000大约需要5秒钟),并且

for (var n=10000;n>0;n--) {
  var b = [];for (var add=10000;add>0;add--) {
    b.push(undefined);
  }
}

超过一分钟(同一Chrome控制台上的10000大约需要90秒,或者慢2000倍)。这不仅是分配,还包括10000次推送,for循环等。


如果每次到达数组末尾都将其复制,则插入n个值的复杂度仍为O(n),因此复杂度没有差异(但速度较慢)。
Tomer Wolberg

很好@TomerWolberg,我改变了措辞。推送方法的复杂性是否比单个成员的逐个切片的初始化/复制要高。AFAIK他们都是固定的时间,至少它们是固定的,所以我改用了“速度”一词。
威尔斯

8

(这可能是更好的评论,但时间太长了)

因此,看完这篇文章后,我很好奇预分配实际上是否更快,因为从理论上讲应该如此。但是,此博客提供了一些建议,建议不要使用它http://www.html5rocks.com/en/tutorials/speed/v8/

因此仍然不确定,我将其进行了测试。事实证明,它似乎实际上要慢一些。

var time = Date.now();
var temp = [];
for(var i=0;i<100000;i++){
    temp[i]=i;
}
console.log(Date.now()-time);


var time = Date.now();
var temp2 = new Array(100000);
for(var i=0;i<100000;i++){
    temp2[i] = i;
}
console.log(Date.now()-time); 

几次随意运行后,此代码将产生以下内容:

$ node main.js 
9
16
$ node main.js 
8
14
$ node main.js 
7
20
$ node main.js 
9
14
$ node main.js 
9
19

3
有趣的是,这似乎是意外的,因此我进行了JSPerf测试。Chrome的结果与您的Node测试相匹配,但是当您为阵列预分配空间时,Firefox和IE会稍微快一些。
Michael Martin-Smucker 2014年

1
@ MichaelMartin-Smucker等待...这是否意味着V8尚未完全优化和完善?!?!?:P
Zeb McCorkle 2014年

1
@ MichaelMartin-Smucker-我刚刚在Chrome版本42.0.2311.90中运行了jsperf(具体来说-在Windows Server 2008 R2 / 7 64位版本的Chrome 42.0.2311.90 32位版本中进行测试),动态大小的阵列速度降低了69% 。
Yellen

1
同上。(我写了原始的节点测试)在动态大小为chrome的chrome 42.0.2311.90中,速度慢了81%:)。但是我们最初的测试是一年多以前了。时间一直在
滑动,滑动

1
令人着迷...从jsperf的结果来看,预分配的功能似乎在Chrome 38中得到了巨大的推动。未来!
Michael Martin-Smucker

8
var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6

2
是的,但是console.log(arr)给出的[5: 0]是一个稀疏数组,可能不是所需的数组。
dreftymac

7

这是另一种解决方案

var arr = Array.apply( null, { length: 4 } );
arr;  // [undefined, undefined, undefined, undefined] (in Chrome)
arr.length; // 4

的第一个参数apply()是this对象绑定,此处我们不在乎,因此将其设置为null

Array.apply(..)正在调用该Array(..)函数并将{ length: 3 }对象值散布为其参数。


我不明白为什么这个答案很糟糕。这实际上是一个不错的技巧。这意味着您不能new Array(n)用于初始化像这样的数组new Array(n).map(function(notUsed,index){...}),但是可以使用@zangw提到的这种方法来进行初始化。向我+1。
Victor.Palyvoda '16

3

数组构造函数的语法不明确,JSLint毕竟会伤害您的感觉。

此外,示例代码已损坏,第二条var语句将引发SyntaxError。您正在设置length数组的属性test,因此不需要另一个var

就您的选择而言,array.length是唯一的“干净”选择。问题是,为什么首先需要设置尺寸?尝试重构您的代码以摆脱这种依赖性。


哎呀,那一刻真是太好了var test。那是我的一些草率的复制和粘贴。
Michael Martin-Smucker

@IvoWetzel,您想要设置大小以提高性能。JS数组是动态数组。如果您没有设置大小(或者说是它的容量),那么JS将分配一个默认大小的数组。如果随后添加的元素数量超出允​​许的数量,则必须扩大数组,这意味着它在内部将分配一个新的数组,然后复制所有元素。
米(Domi)2014年

3

假设数组的长度是恒定的。在Javascript中,这是我们的工作:

const intialArray = new Array(specify the value);


2

如上所述,使用new Array(size)有些危险。而不是直接使用它,而是将其放在“数组创建器函数”中。您可以轻松地确保此函数没有错误,并且避免了new Array(size)直接调用的危险。另外,您可以为其提供可选的默认初始值。该createArray函数正是这样做的:

function createArray(size, defaultVal) {
    var arr = new Array(size);
    if (arguments.length == 2) {
        // optional default value
        for (int i = 0; i < size; ++i) {
            arr[i] = defaultVal;
        }
    }
    return arr;
}

1

在大多数答案中,建议将其推荐给fill数组,因为否则“您不能对其进行迭代”,但这不是事实。您可以迭代一个空数组,而不必使用forEach。While循环,for循环和for i循环工作正常。

const count = Array(5);

不起作用。

console.log('---for each loop:---');
count.forEach((empty, index) => {
    console.log(`counting ${index}`);
});

这些工作:

console.log('---for of loop:---');
for (let [index, empty] of count.entries()) {
  console.log(`counting for of loop ${index}`);
}

console.log('---for i loop:---');
for (let i = 0, il = count.length; i < il; ++i) {
  console.log(`counting for i loop ${i}`);
}

console.log('---while loop:---');
let index = 0;
while (index < count.length) { 
  console.log(`counting while loop ${index}`); 
  index++; 
}

用上面的例子检查这个小提琴

角度也*ngFor适用于空数组:

<li *ngFor="let empty of count; let i = index" [ngClass]="
  <span>Counting with *ngFor {{i}}</span>
</li>

-1

您可以使用以下命令设置数组长度 array.length = youValue

原来如此

var myArray = [];
myArray.length = yourValue;

-3

new Array此代码演示了不应该使用的原因:

var Array = function () {};

var x = new Array(4);

alert(x.length);  // undefined...

其他一些代码可能会使Array变量混乱。我知道有人会写这样的代码有点牵强,但仍然...

另外,正如Felix King所说,该界面有点不一致,并且可能导致一些非常难以跟踪的错误。

如果您想要一个长度为x且未定义的数组(new Array(x)将这样做),则可以执行以下操作:

var x = 4;
var myArray = [];
myArray[x - 1] = undefined;

alert(myArray.length); // 4

48
根据这种推理,您不应使用alertundefined,因为其他一些代码可能会使它们混乱。第二个示例比的可读性差new Array(4),并且不会给您相同的结果:jsfiddle.net/73fKd
Alexey Lebedev

25
这个答案很奇怪
Marco Demaio 2015年

6
您无法避免人们在JavaScript中惹恼全局对象。根本不这样做,或者使用人们可以做到的框架。
理查德·康纳马切

当前在chrome repl中:j = new Array(4); 长度 //结果为4
Parris
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.