索引集合
本章介绍按索引值排序的数据集合。包括数组和类数组结构,如 Array 对象和 TypedArray 对象。
数组是由名称和索引引用的值构成的有序列表。
例如,考虑一个名为 emp 的数组,它包含按数字雇员编号索引的雇员姓名。所以 emp[0] 是 0 号员工,emp[1] 是 1 号员工,以此类推。
JavaScript 中没有明确的数组数据类型。但是,你可以使用预定义的 Array 对象及其方法来处理应用程序中的数组。Array 对象具有以各种方式操作数组的方法,例如连接、反转和排序。它有一个用于确定数组长度的属性和用于正则表达式的其他属性。
在这篇文章中,我们将专注于数组,但许多相同的概念也适用于类型化数组,因为数组和类型化数组共享许多类似的方法。关于类型化数组的更多信息,请参见类型化数组参考。
创建数组
以下语句创建了等效的数组:
const arr1 = new Array(element0, element1, /* … ,*/ elementN);
const arr2 = Array(element0, element1, /* … ,*/ elementN);
const arr3 = [element0, element1, /* … ,*/ elementN];
element0, element1, …, elementN 是数组元素的值列表。当指定这些值时,数组将用它们作为数组的元素初始化。数组的 length 属性被设置为参数的数量。
括号语法称为“数组字面量”或“数组初始化式”。它比其他形式的数组创建更短,因此通常是首选。详见数组字面量。
为了创建一个长度不为 0,但是又没有任何元素的数组,可选以下任何一种方式:
// 这种方式...
const arr1 = new Array(arrayLength);
// ...与这种方式会形成相同数组
const arr2 = Array(arrayLength);
// 这个效果也一样
const arr3 = [];
arr3.length = arrayLength;
备注:以上代码,arrayLength 必须为一个 Number。否则,将会创建一个只有单个元素(内含提供的值)的数组。调用 arr.length 会返回 arrayLength,但数组不包含任何元素。for...in 循环在数组上找不到任何属性。
除了上面所示的新定义的变量外,数组还可以被赋值为新对象或现有对象的属性:
const obj = {};
// …
obj.prop = [element0, element1, /* … ,*/ elementN];
// 或
const obj = { prop: [element0, element1, /* … ,*/ elementN] };
如果你希望用单个元素初始化一个数组,而这个元素恰好又是 Number,那么你必须使用括号语法。当单个 Number 传递给 Array() 构造函数时,将会被解释为 arrayLength,并非单个元素。
// 创建一个只有唯一元素的数组:数字 42。
const arr = [42];
// 创建一个没有元素的数组,且数组的长度被设置成 42。
const arr = Array(42);
// 上面的代码与下面的代码等价:
const arr = [];
arr.length = 42;
如果 N 不是一个整数,调用 Array(N) 将会报 RangeError 错误,下面的例子说明了这种行为:
const arr = Array(9.3); // RangeError: Invalid array length
如果你需要创建任意类型的单元素数组,安全的方式是使用数组字面量。或者在向数组添加单个元素之前先创建一个空的数组。
你也可以使用 Array.of 静态方法来创建包含单个元素的数组。
const wisenArray = Array.of(9.3); // wisenArray 只包含一个元素:9.3
引用数组元素
因为元素也是属性,你可以使用属性访问器来访问。假设你定义了以下数组:
const myArray = ["Wind", "Rain", "Fire"];
你可以将数组的第一个元素引用为 myArray[0],将数组的第二个元素引用为 myArray[1],等等...元素的索引从零开始。
备注:你也可以使用属性访问器来访问数组的其他属性,就像对象一样。
const arr = ["one", "two", "three"];
arr[2]; // three
arr["length"]; // 3
填充数组
你可以通过给数组元素赋值来填充数组,例如:
const emp = [];
emp[0] = "Casey Jones";
emp[1] = "Phil Lesh";
emp[2] = "August West";
备注:如果你在以上代码中给数组运算符的是一个非整型数值,那么它将作为一个表示数组的对象的属性创建,而不是数组的元素。
const arr = [];
arr[3.4] = "Oranges";
console.log(arr.length); // 0
console.log(Object.hasOwn(arr, 3.4)); // true
你也可以在创建数组的时候去填充它:
const myArray = new Array("Hello", myVar, 3.14159);
// 或
const myArray = ["Mango", "Apple", "Orange"];
理解 length
在实现层面,JavaScript 实际上是将元素作为标准的对象属性来存储,把数组索引作为属性名。
length 属性是特殊的,如果存在最后一个元素,则其值总是大于其索引的正整数(在下面的示例中,'Dusty' 的索引是 30,所以 cats.length 返回 30 + 1)。
记住,JavaScript 数组索引是基于 0 的:它们从 0 开始,而不是 1。这意味着 length 属性将比最大的索引值大 1:
const cats = [];
cats[30] = ["Dusty"];
console.log(cats.length); // 31
你也可以给 length 属性赋值。
写一个小于数组元素数量的值将截断数组,写 0 会彻底清空数组:
const cats = ["Dusty", "Misty", "Twiggy"];
console.log(cats.length); // 3
cats.length = 2;
console.log(cats); // [ 'Dusty', 'Misty' ] - Twiggy 已经被移除了
cats.length = 0;
console.log(cats); // 输出 [],猫名称的数组现在已经空了
cats.length = 3;
console.log(cats); // 输出 [ <3 empty items> ]
遍历数组
一种常见的操作是遍历数组的值,以某种方式处理每个值。最简单的方法如下:
const colors = ["red", "green", "blue"];
for (let i = 0; i < colors.length; i++) {
console.log(colors[i]);
}
如果你确定数组中没有一个元素求值为 false(例如,如果你的数组只包含 DOM 节点),你可以选择一个更高效的土法子:
const divs = document.getElementsByTagName("div");
for (let i = 0, div; (div = divs[i]); i++) {
/* 以某种方式处理 div */
}
这避免了检查数组长度的开销,并确保 div 变量在每次循环时都被重新赋值给当前项,从而增加了便利性。
forEach() 方法提供了遍历数组元素的其他方法:
const colors = ["red", "green", "blue"];
colors.forEach((color) => console.log(color));
// red
// green
// blue
传递给 forEach 的函数对数组中的每个元素执行一次,数组元素作为参数传递给该函数。未赋值的值不会在 forEach 循环迭代。
注意,在数组定义时省略的元素不会在 forEach 遍历时被列出,但是手动赋值为 undefined 的元素是会被列出的: