Skip to content

JavaScript 数组

JavaScript 数组(arrays)是灵活有序的集合,用于在单个变量中存储多个值(可以是任何类型)。它们是用于管理列表项的基本数据结构。

最常见且推荐创建数组的方法是使用数组字面量(array literal)[]:

const fruits = ["Apple", "Banana", "Cherry"];
const numbers = [1, 2, 3, 4, 5];
const mixedArray = ["Hello", 100, true, { name: "John" }, ["nested", "array"]];
const emptyArray = [];

在数组字面量中,为了提高可读性,空格和换行符并不重要:

const cars = [
"Saab",
"Volvo",
"BMW"
];

末尾逗号(例如,[1, 2, 3,])在现代 JavaScript 环境中是允许且通常安全的,但在旧版 IE 中可能存在问题。

使用 new Array()(不太常见,可能存在陷阱)

Section titled “使用 new Array()(不太常见,可能存在陷阱)”

你也可以使用 Array 构造函数来创建数组:

const arr1 = new Array("Saab", "Volvo", "BMW"); // 创建一个包含这些元素的数组
const arr2 = new Array(10, 20, 30);
// 陷阱:如果 new Array() 只传入一个数字参数,
// 它将创建一个具有该长度(空槽)的数组,而不是一个包含该数字作为元素的数组。
const arrLength5 = new Array(5); // 创建一个长度为 5 的空数组:[empty x 5]
console.log(arrLength5.length); // 5
console.log(arrLength5[0]); // undefined
// 要创建一个只包含一个数字元素的数组:
const singleNumArr = [42]; // 推荐方式
// const singleNumArrAlt = new Array(42); // 这会创建一个长度为 42 的数组!
// 为了简洁性、可读性并避免陷阱,几乎总是优先使用数组字面量 `[]`。

数组元素通过零基索引(zero-based indexing)访问(第一个元素位于索引 0)。

const colors = ["Red", "Green", "Blue"];
console.log(colors[0]); // 输出: "Red"
console.log(colors[1]); // 输出: "Green"
console.log(colors[2]); // 输出: "Blue"
console.log(colors[3]); // 输出: undefined (索引越界)

你可以通过为其索引赋值来改变元素的值:

colors[1] = "Yellow";
console.log(colors); // 输出: ["Red", "Yellow", "Blue"]

数组具有许多内置属性(properties)和方法(methods),这使得它们功能强大。

length 属性返回数组中元素的数量。它总是比最高索引大一。

const numbers = [10, 20, 30, 40];
console.log(numbers.length); // 输出: 4
// length 属性是可写的(writable)。
// 将其设置为较小的值会截断(truncate)数组。
// 将其设置为较大的值会创建稀疏(sparse)数组(空槽)。
numbers.length = 2;
console.log(numbers); // 输出: [10, 20]
console.log(numbers.length); // 输出: 2

添加和删除元素(可变方法 - Mutating Methods)

Section titled “添加和删除元素(可变方法 - Mutating Methods)”
  • push(item1, ..., itemN): 在数组末尾添加一个或多个元素,并返回新的长度。
  • pop(): 移除数组的最后一个元素,并返回该元素。
  • unshift(item1, ..., itemN): 在数组开头添加一个或多个元素,并返回新的长度。
  • shift(): 移除数组的第一个元素,并返回该元素。
const tasks = ["Write code"];
tasks.push("Test code", "Deploy code"); // tasks 现在是 ["Write code", "Test code", "Deploy code"]
const removedTask = tasks.pop(); // removedTask 是 "Deploy code",tasks 现在是 ["Write code", "Test code"]
tasks.unshift("Plan features"); // tasks 现在是 ["Plan features", "Write code", "Test code"]
const firstTask = tasks.shift(); // firstTask 是 "Plan features",tasks 现在是 ["Write code", "Test code"]

splice(startIndex, deleteCount, item1, ..., itemN): 一个多用途方法,用于添加、移除或替换元素。它修改原数组,并返回一个包含被删除元素的数组。

const months = ["Jan", "March", "April", "June"];
// 在索引 1 处插入 'Feb'
months.splice(1, 0, "Feb");
// months 现在是 ["Jan", "Feb", "March", "April", "June"]
// 将索引 3 处('April')的 1 个元素替换为 'May'
months.splice(3, 1, "May");
// months 现在是 ["Jan", "Feb", "March", "May", "June"]
// 移除索引 0 处的 1 个元素
const deleted = months.splice(0, 1);
// deleted 是 ["Jan"],months 现在是 ["Feb", "March", "May", "June"]
for (let i = 0; i < fruits.length; i++) {
console.log(fruits[i]);
}

对数组中的每个元素执行一次提供的函数(callbackFunction)。

fruits.forEach(function(fruit, index, array) {
console.log(`Index ${index}: ${fruit}`);
});
// 使用箭头函数(arrow function):
fruits.forEach((fruit, index) => console.log(`${index}: ${fruit}`));

for...of 循环 (ES6) - 遍历值的首选方式

Section titled “for...of 循环 (ES6) - 遍历值的首选方式”

迭代(iterate)可迭代对象(iterable objects)的值,包括数组。

for (const fruit of fruits) {
console.log(fruit);
}

现代数组方法(非可变 - 返回新数组)

Section titled “现代数组方法(非可变 - 返回新数组)”

这些方法不会改变原数组,而是返回一个新的数组或值。

  • map(callbackFunction): 通过对原数组的每个元素调用一个函数来创建一个新数组。
  • filter(callbackFunction): 创建一个新数组,其中包含通过提供的函数实现的测试的所有元素。
  • reduce(callbackFunction, initialValue): 对每个元素执行一个 reducer 函数,最终得到一个单一的输出值。
  • slice(startIndex, endIndex): 返回数组的一部分的浅拷贝(shallow copy)到一个新数组。endIndex 是不包含的。
  • concat(array2, ..., arrayN): 合并两个或多个数组。返回一个新数组。
  • find(callbackFunction): 返回数组中满足提供的测试函数的第一个元素。
  • findIndex(callbackFunction): 返回数组中满足测试函数的第一个元素的索引。
  • some(callbackFunction): 测试数组中是否至少有一个元素通过了测试。
  • every(callbackFunction): 测试数组中是否所有元素都通过了测试。
  • flat(depth): 创建一个新数组,并将所有子数组元素递归地连接到其中,直到达到指定的深度。
  • flatMap(callbackFunction): 使用映射函数映射每个元素,然后将结果展平(flatten)到一个新数组(深度为 1)。
const nums = [1, 2, 3, 4, 5];
const doubled = nums.map(num => num * 2); // [2, 4, 6, 8, 10]
const evens = nums.filter(num => num % 2 === 0); // [2, 4]
const sum = nums.reduce((accumulator, current) => accumulator + current, 0); // 15
const subArray = nums.slice(1, 3); // [2, 3]
const moreNums = [6, 7];
const combined = nums.concat(moreNums); // [1, 2, 3, 4, 5, 6, 7]
// 使用展开运算符(spread operator)进行合并:const combinedSpread = [...nums, ...moreNums];
const firstEven = nums.find(num => num % 2 === 0); // 2

在 JavaScript 中,数组是一种特殊类型的对象。typeof 运算符对于数组返回 "object"。

console.log(typeof fruits); // 输出: "object"

要可靠地检查变量是否是数组,请使用 Array.isArray():

console.log(Array.isArray(fruits)); // 输出: true
console.log(Array.isArray({a: 1})); // 输出: false

虽然数组是对象,但它们针对数字索引访问进行了优化,而普通对象则针对命名属性进行了优化。

展开运算符(...)和数组解构(ES6)

Section titled “展开运算符(...)和数组解构(ES6)”

展开运算符将可迭代对象(如数组)展开为单独的元素。

const original = [1, 2, 3];
const copy = [...original]; // 创建一个浅拷贝
const merged = [...original, 4, 5, ...moreNums]; // 合并数组
function sumNumbers(a, b, c) { return a + b + c; }
console.log(sumNumbers(...original)); // 将数组元素展开作为参数:sumNumbers(1, 2, 3)

允许将数组中的值解包到不同的变量中。

const [first, second, ...rest] = ["Apple", "Banana", "Cherry", "Date"];
console.log(first); // "Apple"
console.log(second); // "Banana"
console.log(rest); // ["Cherry", "Date"] (剩余运算符(rest operator)收集剩余元素)

有关数组方法的完整指南,请访问 MDN Array 文档。