跳到主要内容

JavaScript 数组

数组是存储有序数据集合的数据结构。本章将详细介绍数组的创建和操作方法。

数组基础

创建数组

// 字面量方式(推荐)
const fruits = ["苹果", "香蕉", "橙子"];
const numbers = [1, 2, 3, 4, 5];
const mixed = [1, "hello", true, null, undefined];

// 构造函数方式
const arr1 = new Array();
const arr2 = new Array(5); // 创建长度为 5 的空数组
const arr3 = new Array(1, 2, 3); // [1, 2, 3]

// Array.of(ES6)- 避免构造函数歧义
const arr4 = Array.of(5); // [5]
const arr5 = Array.of(1, 2, 3); // [1, 2, 3]

// Array.from(ES6)- 从类数组或可迭代对象创建数组
const str = "hello";
const charArray = Array.from(str); // ["h", "e", "l", "l", "o"]

const set = new Set([1, 2, 3]);
const setArray = Array.from(set); // [1, 2, 3]

// 带映射函数
const doubled = Array.from([1, 2, 3], x => x * 2); // [2, 4, 6]

访问数组元素

const fruits = ["苹果", "香蕉", "橙子"];

// 使用索引访问
console.log(fruits[0]); // 苹果
console.log(fruits[1]); // 香蕉
console.log(fruits[2]); // 橙子

// 获取数组长度
console.log(fruits.length); // 3

// 访问最后一个元素
console.log(fruits[fruits.length - 1]); // 橙子

// 越界访问
console.log(fruits[10]); // undefined

数组是引用类型

const arr1 = [1, 2, 3];
const arr2 = arr1; // 引用同一个数组

arr2.push(4);
console.log(arr1); // [1, 2, 3, 4](arr1 也被修改了)

// 浅拷贝
const arr3 = [...arr1]; // 使用展开运算符
const arr4 = arr1.slice();
const arr5 = Array.from(arr1);

数组方法

添加和删除元素

const arr = [1, 2, 3];

// push - 在末尾添加元素(返回新长度)
arr.push(4);
console.log(arr); // [1, 2, 3, 4]

// pop - 移除末尾元素(返回被移除的元素)
const popped = arr.pop();
console.log(popped); // 4
console.log(arr); // [1, 2, 3]

// unshift - 在开头添加元素(返回新长度)
arr.unshift(0);
console.log(arr); // [0, 1, 2, 3]

// shift - 移除开头元素(返回被移除的元素)
const shifted = arr.shift();
console.log(shifted); // 0
console.log(arr); // [1, 2, 3]

// splice - 添加、删除或替换元素
const months = ["Jan", "March", "April", "June"];

// 插入元素
months.splice(1, 0, "Feb");
console.log(months); // ["Jan", "Feb", "March", "April", "June"]

// 删除元素
months.splice(3, 1);
console.log(months); // ["Jan", "Feb", "March", "June"]

// 替换元素
months.splice(3, 1, "May");
console.log(months); // ["Jan", "Feb", "March", "May"]

遍历数组

const numbers = [1, 2, 3];

// for 循环
for (let i = 0; i < numbers.length; i++) {
console.log(numbers[i]);
}

// forEach
numbers.forEach((value, index, array) => {
console.log(`索引 ${index}: ${value}`);
});

// for...of
for (const num of numbers) {
console.log(num);
}

查找元素

const fruits = ["苹果", "香蕉", "橙子", "香蕉"];

// indexOf - 返回第一个匹配元素的索引
console.log(fruits.indexOf("香蕉")); // 1
console.log(fruits.indexOf("葡萄")); // -1

// lastIndexOf - 返回最后一个匹配元素的索引
console.log(fruits.lastIndexOf("香蕉")); // 3

// includes - 检查是否包含元素(ES6)
console.log(fruits.includes("香蕉")); // true
console.log(fruits.includes("葡萄")); // false

// find - 返回第一个满足条件的元素
const numbers = [1, 5, 10, 15];
const firstBig = numbers.find(n => n > 8);
console.log(firstBig); // 10

// findIndex - 返回第一个满足条件的元素索引
const firstBigIndex = numbers.findIndex(n => n > 8);
console.log(firstBigIndex); // 2

数组转换

const numbers = [1, 2, 3, 4, 5];

// map - 转换每个元素
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// filter - 过滤元素
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]

// reduce - 汇总元素
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15

const max = numbers.reduce((acc, n) => n > acc ? n : acc, numbers[0]);
console.log(max); // 5

// flat - 扁平化数组(ES2019)
const nested = [1, [2, 3], [4, [5, 6]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5, 6]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5, 6]
console.log(nested.flat(Infinity)); // 完全扁平化

// flatMap - 先 map 再 flat
const sentences = ["Hello world", "How are you"];
const words = sentences.flatMap(s => s.split(" "));
console.log(words); // ["Hello", "world", "How", "are", "you"]

数组排序

const fruits = ["香蕉", "苹果", "橙子"];

// sort - 就地排序(默认按字符串Unicode码点)
fruits.sort();
console.log(fruits); // ["苹果", "橙子", "香蕉"]

// 自定义排序
const numbers = [10, 2, 30, 1];
numbers.sort((a, b) => a - b); // 升序
console.log(numbers); // [1, 2, 10, 30]

numbers.sort((a, b) => b - a); // 降序
console.log(numbers); // [30, 10, 2, 1]

// 按对象属性排序
const students = [
{ name: "张三", score: 85 },
{ name: "李四", score: 92 },
{ name: "王五", score: 78 }
];
students.sort((a, b) => b.score - a.score);
console.log(students); // 按分数降序排列

// reverse - 反转数组
numbers.reverse();
console.log(numbers); // [1, 2, 10, 30]

数组连接和切片

const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [5, 6];

// concat - 连接数组
const combined = arr1.concat(arr2, arr3);
console.log(combined); // [1, 2, 3, 4, 5, 6]

// 展开运算符(更常用)
const spreadCombined = [...arr1, ...arr2, ...arr3];
console.log(spreadCombined); // [1, 2, 3, 4, 5, 6]

// slice - 切片(不修改原数组)
const numbers = [1, 2, 3, 4, 5];
console.log(numbers.slice(1, 3)); // [2, 3]
console.log(numbers.slice(2)); // [3, 4, 5]
console.log(numbers.slice(-2)); // [4, 5]
console.log(numbers.slice()); // 浅拷贝整个数组

数组判断

const numbers = [1, 2, 3, 4, 5];

// some - 是否存在满足条件的元素
const hasEven = numbers.some(n => n % 2 === 0);
console.log(hasEven); // true

// every - 是否所有元素都满足条件
const allPositive = numbers.every(n => n > 0);
console.log(allPositive); // true

// Array.isArray - 判断是否为数组
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray("hello")); // false

常用数组操作

搜索和过滤

const students = [
{ name: "张三", score: 85 },
{ name: "李四", score: 92 },
{ name: "王五", score: 78 },
{ name: "赵六", score: 95 }
];

// 找出最高分学生
const topStudent = students.reduce((top, s) =>
s.score > top.score ? s : top
);
console.log(`最高分:${topStudent.name} - ${topStudent.score}`);

// 找出及格的学生
const passedStudents = students.filter(s => s.score >= 60);
console.log(passedStudents);

// 按分数排序
const sortedStudents = [...students].sort((a, b) => b.score - a.score);
console.log(sortedStudents);

// 计算平均分
const avgScore = students.reduce((sum, s) => sum + s.score, 0) / students.length;
console.log(`平均分:${avgScore}`);

去重

const numbers = [1, 2, 2, 3, 3, 3, 4, 4, 5];

// 使用 Set
const unique1 = [...new Set(numbers)];
console.log(unique1); // [1, 2, 3, 4, 5]

// 使用 filter
const unique2 = numbers.filter((item, index) => numbers.indexOf(item) === index);
console.log(unique2); // [1, 2, 3, 4, 5]

// 使用 reduce
const unique3 = numbers.reduce((acc, item) =>
acc.includes(item) ? acc : [...acc, item], []
);
console.log(unique3); // [1, 2, 3, 4, 5]

数组交集、并集、差集

const a = [1, 2, 3, 4];
const b = [3, 4, 5, 6];

// 并集
const union = [...new Set([...a, ...b])];
console.log(union); // [1, 2, 3, 4, 5, 6]

// 交集
const intersection = a.filter(item => b.includes(item));
console.log(intersection); // [3, 4]

// 差集 (a - b)
const diffAB = a.filter(item => !b.includes(item));
console.log(diffAB); // [1, 2]

// 对称差集 (a ∪ b - a ∩ b)
const symDiff = [...a.filter(item => !b.includes(item)),
...b.filter(item => !a.includes(item))];
console.log(symDiff); // [1, 2, 5, 6]

二维数组

// 创建二维数组
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];

// 访问元素
console.log(matrix[0][0]); // 1
console.log(matrix[1][2]); // 6

// 遍历二维数组
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(`matrix[${i}][${j}] = ${matrix[i][j]}`);
}
}

// 创建指定大小的二维数组
const rows = 3, cols = 4;
const emptyMatrix = Array.from({ length: rows }, () => Array(cols).fill(0));
console.log(emptyMatrix);
// [
// [0, 0, 0, 0],
// [0, 0, 0, 0],
// [0, 0, 0, 0]
// ]

链式调用

const orders = [
{ id: 1, product: "手机", quantity: 2, price: 2999 },
{ id: 2, product: "耳机", quantity: 1, price: 199 },
{ id: 3, product: "充电器", quantity: 3, price: 99 },
{ id: 4, product: "手机壳", quantity: 5, price: 29 }
];

// 计算订单总金额
const total = orders
.filter(order => order.quantity >= 2) // 过滤数量>=2的订单
.map(order => order.quantity * order.price) // 计算每笔订单金额
.reduce((sum, amount) => sum + amount, 0); // 汇总

console.log(`总金额:${total}`); // 6394

小结

本章我们学习了:

  1. 数组的创建方法
  2. 访问和修改数组元素
  3. 添加和删除元素(push、pop、shift、unshift、splice)
  4. 遍历数组(for、forEach、for...of)
  5. 查找元素(indexOf、find、findIndex)
  6. 数组转换(map、filter、reduce、flat)
  7. 数组排序和反转
  8. 数组连接和切片
  9. 常用数组操作(去重、交集、并集、差集)

练习

  1. 创建一个函数,接受一个数组,返回一个新数组,只包含偶数
  2. 创建一个函数,计算数组的平均值
  3. 实现数组扁平化函数 flatten(arr)
  4. 实现数组去重函数 unique(arr)
  5. 实现数组排序函数(不使用原生 sort)