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

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

在我的js文件中大量使用这种语法后,我通过jsLint运行其中一个文件,它吓坏了:

错误:第1行字符22的问题:预期')',而不是看到'4'。
var test=new数组(4);
第1行字符23的问题:预期';',而不是看到')'。
var test=new数组(4);
第1行第23个字符的问题:需要一个标识符,而是看到 ')'.

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

所以我有几个问题:

首先,为什么?我改用new Array()语法有任何风险吗?是否有我应该注意的浏览器不兼容问题?

其次,如果我切换到方括号语法,有没有办法在一行上声明一个数组并设置它的长度,或者我必须这样做:

var test = [];test.length = 4;
1193625 次浏览
  1. 为什么要初始化长度?理论上不需要这样做。它甚至可能导致混乱的行为,因为所有使用length来确定数组是否为空的测试都会报告数组不为空。
    一些测试表明,如果之后填充数组,设置大数组可以的初始长度会更有效,但性能增益(如果有)似乎因浏览器而异。

  2. jsLint不喜欢new Array(),因为构造器是模棱两可的。

    new Array(4);

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

    new Array('4');

    创建一个数组包含值'4'

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

var data = [];var length = 5; // user defined length
for(var i = 0; i < length; i++) {data.push(createSomeObject());}

你不应该使用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

数组构造函数有一个歧义语法,JSLint毕竟伤害了你的感情。

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

就你的选项而言,array.length是唯一“干净”的选项。问题是,为什么你首先需要设置大小?尝试重构你的代码以摆脱这种依赖关系。

这将初始化长度属性为4:

var x = [,,,,];

稀疏数组在这里!🥳[2021]

在现代JS引擎中,完全支持稀疏数组。您可以以任何您喜欢的方式使用[]new Array(len),即使是随机访问。字典模式似乎已经成为过去。

在当前Chrome(我猜任何V8环境),数组的长度可以达到2^32-1,分配是稀疏的(这意味着空块不会占用任何内存):

在此处输入图片描述

在此处输入图片描述

然而,有一个陷阱

一方面,for循环按预期工作,然而,Array的内置高阶函数(例如mapfilterfindsome等)忽略未赋值元素。它们首先需要fill(或其他一些填充方法):

const a = new Array(10);const b = new Array(10).fill(0);
a.forEach(x => console.log(x)); // does nothingb.forEach(x => console.log(x)); // works as intended

旧版

(我删除了大部分旧版本。)要点是使用new Array(largeNumber)创建一个大数组或在尚未分配的地方随机访问一个数组将使其陷入“字典模式”。这意味着你使用的是一个带有索引的数组,但实际上它将使用字典来存储值,从而扰乱性能和迭代行为。幸运的是,这已经成为过去。

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

所以,读了这篇文章后,我很好奇预分配是否真的更快,因为理论上它应该是。然而,这个博客给出了一些建议反对它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.js916$ node main.js814$ node main.js720$ node main.js914$ node main.js919

我很惊讶没有一个函数式的解决方案建议允许您在一行中设置长度。以下基于UnderScoreJS:

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

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

var arr=[];arr[5]=0;alert("length="+arr.length); // gives 6
  • 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]

这是另一个解决方案

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 }对象值作为其参数展开。

请不要放弃你的旧习惯。在分配内存一次然后处理该数组中的条目(如旧的)和在数组增长时多次分配内存(这不可避免地是系统使用其他建议的方法所做的)之间的速度存在很大差异。

当然,这些都不重要,除非你想用更大的数组做一些很酷的事情。

鉴于目前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循环等。

使用ES2015#0,您现在可以简单地执行:

// `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)要简洁得多

可以在.fill()中删除0并在没有参数的情况下运行,这将用undefined填充数组。(但是,这在TypeScript中会失败

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

所以应该是

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

最短的:

let arr = [...Array(10)];console.log(arr);

ES6引入了Array.from,它允许您从任何类数组可迭代对象对象创建Array

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

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

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

[...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]

创建嵌套数组

当使用fill创建2D数组时,直观地应该创建新实例。但实际上会发生的是相同的数组将被存储为引用。

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] ]

数学填充值

使用数学时方便的小方法重载

function NewArray( size , method, linear ){method = method || ( i => i );linear = linear || false;var x = [];for( var i = 0; i < size; ++i )x[ i ] = method( linear ? i / (size-1) : i );return x;}
NewArray( 4 );// [ 0, 1, 2, 3 ]
NewArray( 4, Math.sin );// [ 0, 0.841, 0.909, 0.141 ]
NewArray( 4, Math.sin, true );// [ 0, 0.327, 0.618, 0.841 ]
var pow2 = ( x ) => x * x;
NewArray( 4, pow2 );// [ 0, 1, 4, 9 ]
NewArray( 4, pow2, true );// [ 0, 0.111, 0.444, 1 ]

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

const intialArray = new Array(specify the value);

在大多数答案中,建议将数组fill,因为否则"你不能迭代它",但这不是真的。您可以迭代一个空数组,但不能使用forEach。而循环、for of循环和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++;}

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

同样,angulars*ngFor可以很好地处理空数组:

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

除了其他人的答案之外,另一个聪明的方法是使用Float32Array创建一个数组并对其进行迭代。

为此,从Float32Array创建一个具有所需长度的实例,如下所示:

new Float32Array(5)

此代码返回一个类似数组的数组,您可以将其转换为Array.from()的数组:

Array.from(new Float32Array(5)) // [0, 0, 0, 0, 0]

您还可以使用fill()更改项目的值:

Array.from(new Float32Array(5).fill(2)) // [2, 2, 2, 2, 2]

当然,你可以迭代它:

Array.from(new Float32Array(5)).map(item => /* ... */ )

最简单的方法是使用

Array.from({ length: 3 });
// gives you[undefined, undefined, undefined]

Array(3)不同,它会给你一个你不能迭代的数组。Array.from({ length })给你一个可以轻松迭代的数组。

Array.from({ length: 3 }).map((e, idx) => `hi ${idx}`);// ['hi 1', 'hi 2', 'hi 3']