我读过的关于 JavaScript 数组(包括 w3schools 和 devguru)的大多数教程都建议您可以通过使用 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's explanation of its behavior 后,看起来 jsLint 并不真正喜欢 new Array()
语法,而是在声明数组时更喜欢 []
。
所以我有几个问题:
首先,为什么?改用 new Array()
语法会带来任何风险吗?是否存在我应该注意的浏览器不兼容问题?
其次,如果我切换到方括号语法,有没有办法声明一个数组并将它的长度全部设置在一行上,或者我必须做这样的事情:
var test = [];
test.length = 4;
new Array()
,但可以指定大小。我认为这一切都归结为整个上下文的代码一致性。
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) // 给出 [0, 1, 2, 3, 4]
为什么要初始化长度?理论上没有必要这样做。它甚至会导致令人困惑的行为,因为所有使用长度来确定数组是否为空的测试都会报告该数组不为空。一些测试表明,如果稍后填充数组,则设置大型数组的初始长度可能会更有效,但性能增益(如果有的话)似乎因浏览器而异。 jsLint 不喜欢 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());
}
for
循环,该循环遍历数组的长度并填充它。我想在 JavaScript 中还有其他方法可以做到这一点,所以真正回答“我为什么要这样做?”是“因为用其他语言编程时形成的旧习惯。” :)
为什么要初始化长度?理论上没有必要这样做。并且所有使用长度来确定数组是否为空的测试都将失败。嗯,也许是性能?设置数组的预先存在的元素比动态添加它要快。
new Array(10)
不会创建具有 10 个未定义元素的数组。它只是创建一个长度为 10 的空数组。请参阅此答案以了解令人讨厌的事实:stackoverflow.com/questions/18947892/…
使用 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)
简洁得多
可以将 0
放在 .fill()
中并在不带参数的情况下运行,这将用 undefined
填充数组。 (然而,这在 Typescript 中会失败)
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
Array(n).fill()
[...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 ]
a[0].push(9);
// [ [6, 9], [6], [6] ]
应该是 a[0].push(9);
// [ [6, 9], [6, 9], [6, 9] ]
,因为每个嵌套数组都存储为引用,因此改变一个数组会影响其余数组。虽然我认为你可能只是打错了:)
[...Array(6)].map(x => 0);
是一个很好的解决方案,对我很有帮助!稍作修改,它就可以用来创建一个对整数进行计数的数组(就像你的打字稿安全示例一样):[...Array(6)].map((x, i) => i);
结果为 [0, 1, 2, 3, 4, 5]
map((_, i) => i)
Array(Infinity).fill(0)
创造你的宇宙! Array(Infinity).fill(Infinity)
你是上帝!
最短的:
让 arr = [...Array(10)];控制台.log(arr);
[
开始一行而不使用 ;
,因为它会尝试取消引用上面的行。因此,在代码的任何部分可预见地使用此行的安全方法是:;[...Array(1000)]//.whateverOpsNeeded()
[...Array(5)].map((item, index) => ({ index }))
也试试这个:[...Array(5)]; console.log('hello');
const a = 'a'
,下一行是 [...Array(5)].//irrelevent
。你认为第一行会解决什么问题?它将是 const a = 'a'[...Array(5)]
,这将导致:Uncaught SyntaxError: Unexpected token ...
const a = 'a'
将是一个 Lint 错误。无论如何,这与这个答案无关。
ES6 引入了 Array.from
,它允许您从任何 "array-like" 或 iterables 对象创建一个 Array
:
Array.from({length: 10}, (x, i) => i);
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
在这种情况下,{length: 10}
表示 “类数组” 对象的最小定义:一个只定义了 length
属性的空对象。
Array.from
允许第二个参数映射到结果数组。
稀疏数组来了! 🥳 [2021]
在现代 JS 引擎中,完全支持稀疏数组。您可以以任何您喜欢的方式使用 []
或 new Array(len)
,即使是随机访问。字典模式似乎已成为过去。
在当前的 Chrome 中(我猜是任何 V8 环境),数组的长度可以达到 2^32-1 并且分配是稀疏的(意味着空块不会占用任何内存):
https://i.stack.imgur.com/aEmtE.png
https://i.stack.imgur.com/db75k.png
但是,有一个问题
一方面,for
循环按预期工作,但是,Array
的内置高阶函数(例如 map
、filter
、find
、some
等)忽略未分配元素。他们首先需要 fill
(或其他一些填充方法):
const a = new Array(10);
const b = new Array(10).fill(0);
a.forEach(x => console.log(x)); // does nothing
b.forEach(x => console.log(x)); // works as intended
旧版
(我删除了大部分旧版本。)要点是使用 new Array(largeNumber)
创建一个大数组或在尚未分配的地方随机访问一个数组会使它陷入“字典模式”。这意味着您正在使用带有索引的数组,但在引擎盖下它会使用字典来存储值,因此会影响性能以及迭代行为。幸运的是,这已成为过去。
let
、var
或 const
)。我只是想为下一个复制 StackOverflow 代码并意识到它不起作用的人清理一下😉
.fill
而不带参数,然后您可以遍历数组:new Array(10).fill().forEach(i => console.log(i))
这会将长度属性初始化为 4:
var x = [,,,,];
var size = 42; var myArray = eval("["+",".repeat(size)+"]");
怎么样? (没那么严重;)
我很惊讶没有建议的功能性解决方案允许您在一行中设置长度。以下基于UnderscoreJS:
var test = _.map(_.range(4), function () { return undefined; });
console.log(test.length);
由于上述原因,除非我想将数组初始化为特定值,否则我会避免这样做。有趣的是,还有其他实现 range 的库,包括 Lo-dash 和 Lazy,它们可能具有不同的性能特征。
这是另一个解决方案
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 来自我。
Array.apply(null, { length: childrenLength }).map((notUsed, i) => (<div key={i} />)
请人们不要放弃你的旧习惯。分配内存一次然后使用该数组中的条目(与旧的一样)与随着数组的增长多次分配内存之间存在很大的速度差异(这不可避免地是系统在使用其他建议的方法时所做的) .
当然,这些都不重要,除非你想用更大的数组做一些很酷的事情。然后它会。
鉴于目前 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);
}
有一些权衡:
该方法第一次调用该函数所需的时间与其他方法一样长,但以后调用的时间很少(除非要求更大的数组)。
标准阵列会永久保留与您要求的最大阵列一样多的空间。
但是,如果它与您正在做的事情相符,则可能会有所回报。非正式定时看跌期权
for (var n=10000;n>0;n--) {var b = newArrayWithSize(10000);b[0]=0;}
非常快(考虑到 n=1000000 大约需要 5 秒,10000 大约需要 50 毫秒),并且
for (var n=10000;n>0;n--) {
var b = [];for (var add=10000;add>0;add--) {
b.push(undefined);
}
}
超过一分钟(10000 在同一个 chrome 控制台上大约需要 90 秒,或者慢大约 2000 倍)。这不仅仅是分配,还有 10000 次推送、for 循环等。
(这可能作为评论更好,但太长了)
所以,读完这篇文章后,我很好奇预分配是否真的更快,因为理论上它应该是。但是,此博客提供了一些反对它的提示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
var arr=[];
arr[5]=0;
alert("length="+arr.length); // gives 6
[5: 0]
这是一个稀疏数组,可能不是想要的。
js const foo = []; foo[10] = undefined;
foo
的值不是 [undefined, undefined undefined, ...]
而是 [empty * 9, undefined]
。如果您尝试运行任何顺序数组方法(forEach
、map
、reduce
),那么您会意识到它实际上并没有遍历空项。它们只是死角。
假设 Array 的长度是恒定的。在 Javascript 中,这就是我们所做的:
const intialArray = new Array(specify the value);
数组构造函数有一个 ambiguous syntax,毕竟 JSLint 只会伤害你的感受。
此外,您的示例代码已损坏,第二个 var
语句将引发 SyntaxError
。您正在设置数组 test
的属性 length
,因此不需要另一个 var
。
就您的选择而言,array.length
是唯一“干净”的选项。问题是,为什么首先需要设置大小?尝试重构您的代码以摆脱这种依赖关系。
var test
。那是我草率的复制和粘贴。
除了其他人的答案,另一个聪明的方法是使用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 => /* ... */ )
在大多数答案中,建议 fill
数组,否则“你不能迭代它”,但事实并非如此。您可以迭代一个空数组,但不能使用 forEach
。 While 循环、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++;
}
使用上述示例检查 this fiddle。
angulars *ngFor
也适用于空数组:
<li *ngFor="let empty of count; let i = index" [ngClass]="
<span>Counting with *ngFor {{i}}</span>
</li>
您可以使用 array.length = youValue
设置数组长度
所以它会是
var myArray = [];
myArray.length = yourValue;
以下代码说明了您不应使用 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
alert
和 undefined
,因为其他一些代码可能会混淆它们。第二个示例比 new Array(4)
可读性差,并且不会给您相同的结果:jsfiddle.net/73fKd
Array
有多个参数时,它会遍历arguments
对象并将每个值显式应用于新数组。当您使用数组或具有长度属性的对象调用Array.apply
时,Array
将使用长度显式设置新数组的每个值。这就是为什么Array(5)
给出了 5 个省略的数组,而Array.apply(null, Array(5))
给出了 5 个未定义的数组。有关详细信息,请参阅 this 答案。Array.apply(null, Array(5))
也可以写成Array.apply(null, {length: 5})
。确实没有太大区别,但是后者很明确,其意图是创建一个length 5
的数组,而不是一个包含5
的数组