JavaScript 数组完全指南:从入门到实战
前言
数组是 JavaScript 中最常用的数据结构之一,也是面试中的高频考点。本文将从实际代码出发,系统讲解 JavaScript 数组的核心概念、常用方法和高级技巧,帮助你彻底掌握这一重要知识点。
一、认识数组
1.1 什么是数组
数组(Array)是一段连续的存储空间,用于存储有序的元素集合。在 JavaScript 中,数组是「开箱即用」的数据结构,无需额外引入。
javascript
const arr = [1, 2, 3, 4, 5];
console.log(arr); // [1, 2, 3, 4, 5]
JavaScript 的数组非常灵活:
- 类型不限:数组元素可以是任意类型
- 长度可变:无需预先声明固定长度
javascript
const mixedArr = [1, 'hello', true, { name: 'Alice' }, [1, 2, 3]];
1.2 内存视角
数组的底层是连续的内存空间,通过「起始地址 + 偏移量」快速定位元素。这使得数组的随机访问效率极高,时间复杂度为 O(1)。
javascript
const arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr[0]); // 'a' - 通过索引快速访问
console.log(arr[3]); // 'd'
1.3 ADT 抽象数据类型
在计算机科学中,我们常用 ADT(Abstract Data Type,抽象数据类型) 来描述数据结构:
连续的存储空间 + 特定的操作
以数组为例:
- 存储空间 :
[1, 2, 3, 4, 5] - 特定操作 :
push、pop、shift、unshift等
这种抽象方式让我们专注于「做什么」,而不必关心「怎么做」。
二、数组的创建方法
2.1 字面量创建
最常用、最简洁的方式:
javascript
const arr = [1, 2, 3, 4, 5];
2.2 构造函数创建
使用 new Array() 构造函数:
javascript
const arr = new Array(); // 空数组
const arr2 = new Array(7); // 指定长度为7的空数组
console.log(arr2); // [empty × 7]
注意 :new Array(7) 创建的是「空位置」,不是 undefined:
javascript
const arr = new Array(7);
console.log(arr[0]); // undefined
console.log(arr.length); // 7
2.3 fill 方法初始化
对于需要初始值的数组,使用 fill() 方法:
javascript
const arr = new Array(7).fill(0);
console.log(arr); // [0, 0, 0, 0, 0, 0, 0]
三、增删操作:push、pop、unshift、shift
JavaScript 数组提供了四个基本增删方法,它们都会修改原数组。
3.1 push - 尾部添加
javascript
const arr = ['a', 'b', 'c'];
arr.push('d');
console.log(arr); // ['a', 'b', 'c', 'd']
console.log(arr.push('e')); // 返回新长度: 5
3.2 pop - 尾部删除
javascript
const arr = ['a', 'b', 'c', 'd', 'e'];
console.log(arr.pop()); // 返回被删除元素: 'e'
console.log(arr); // ['a', 'b', 'c', 'd']
3.3 unshift - 头部添加
javascript
const arr = [1, 2, 3];
arr.unshift(0);
console.log(arr); // [0, 1, 2, 3]
3.4 shift - 头部删除
javascript
const arr = [1, 2, 3, 4, 5];
console.log(arr.shift()); // 返回被删除元素: 1
console.log(arr); // [2, 3, 4, 5]
3.5 方法对比
| 方法 | 操作位置 | 返回值 | 是否修改原数组 |
|---|---|---|---|
push |
尾部添加 | 新数组长度 | ✅ |
pop |
尾部删除 | 被删除元素 | ✅ |
unshift |
头部添加 | 新数组长度 | ✅ |
shift |
头部删除 | 被删除元素 | ✅ |
3.6 纯函数概念
以上四个方法都会修改原数组,属于非纯函数。纯函数的特点是:
- 不修改参数
- 相同输入,相同输出
javascript
// 非纯函数 - 修改原数组
function pushImpure(arr, item) {
arr.push(item);
return arr;
}
// 纯函数 - 不修改原数组
function pushPure(arr, item) {
return [...arr, item];
}
四、数组遍历方法
4.1 for 循环
最传统的方式,性能最好但可读性较差:
javascript
const arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
4.2 for...of 循环
语义更好,专为可迭代对象设计:
javascript
const arr = [1, 2, 3, 4, 5];
for (const item of arr) {
console.log(item);
}
4.3 forEach 方法
功能强大,但无法中断遍历 (不支持 break 和 continue):
javascript
const arr = ['苹果', '香蕉', '橙子'];
arr.forEach((item, index, self) => {
console.log(`${index}: ${item}`);
});
回调参数说明:
| 参数 | 说明 |
|---|---|
item |
当前遍历的元素 |
index |
当前元素的索引 |
self |
数组本身 |
五、高阶方法:map、filter、every、some、reduce
这些方法都基于 forEach 实现,都返回新数组 (除了 every、some 返回布尔值),都是纯函数。
5.1 map - 元素转换
将数组中的每个元素映射为新值,返回全新数组:
javascript
const arr = [1, 2, 3, 4, 5];
// 将每个元素乘以2
const doubled = arr.map(item => item * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
// 元素类型转换
const strings = arr.map(item => String(item));
console.log(strings); // ['1', '2', '3', '4', '5']
5.2 filter - 条件筛选
保留满足条件的元素,返回新数组:
javascript
const arr = [1, 2, 3, 4, 5];
// 筛选偶数
const evens = arr.filter(item => item % 2 === 0);
console.log(evens); // [2, 4]
// 筛选大于3的元素
const greaterThan3 = arr.filter(item => item > 3);
console.log(greaterThan3); // [4, 5]
5.3 every - 全量判断
检查是否所有元素都满足条件:
javascript
const arr = [1, 2, 3, 4, 5];
// 检查是否全为正数
console.log(arr.every(item => item > 0)); // true
// 检查是否全为偶数
console.log(arr.every(item => item % 2 === 0)); // false
特点:遇到不满足条件的元素会立即停止(短路)。
5.4 some - 存在判断
检查是否存在至少一个满足条件的元素:
javascript
const arr = [1, 2, 3, 4, 5];
// 是否存在偶数
console.log(arr.some(item => item % 2 === 0)); // true
// 是否全为负数
console.log(arr.some(item => item < 0)); // false
特点:遇到满足条件的元素会立即停止(短路)。
5.5 reduce - 累积归约
将数组元素累积为单一值,是最强大、最灵活的方法:
javascript
const arr = [1, 2, 3, 4, 5];
// 求和
const sum = arr.reduce((pre, cur) => pre + cur);
console.log(sum); // 15
// 求积
const product = arr.reduce((pre, cur) => pre * cur, 1);
console.log(product); // 120
// 带初始值
const sumWithInit = arr.reduce((pre, cur) => pre + cur, 10);
console.log(sumWithInit); // 25(10 + 1 + 2 + 3 + 4 + 5)
参数说明:
| 参数 | 说明 |
|---|---|
pre |
累积结果(初始值为第一次迭代时的 pre) |
cur |
当前元素 |
initialValue |
初始值(可选) |
5.6 方法选择指南
| 场景 | 推荐方法 |
|---|---|
| 元素转换 | map |
| 条件筛选 | filter |
| 全量判断 | every |
| 存在判断 | some |
| 累积计算 | reduce |
5.7 链式调用
多个方法可以链式使用:
javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = numbers
.filter(num => num % 2 === 0) // [2, 4, 6, 8, 10]
.map(num => num * 10) // [20, 40, 60, 80, 100]
.reduce((a, b) => a + b); // 300
console.log(result);
六、原型链与 Array.prototype
在 JavaScript 中,所有数组实例都共享 Array.prototype 上的方法。这就是原型链的体现:
javascript
const arr = new Array();
console.log(typeof Array); // "function" - Array 是构造函数
console.log(Array.prototype); // 数组原型对象
console.log(Array.prototype.__proto__); // Object.prototype
原型链结构
lua
arr (数组实例)
↓ [[Prototype]]
Array.prototype (包含 push, pop, map, filter 等方法)
↓ [[Prototype]]
Object.prototype (所有对象的终极原型)
↓ [[Prototype]]
null (原型链终点)
方法来源
当你调用数组方法时:
javascript
const arr = [1, 2, 3];
arr.push(4); // push 方法来自 Array.prototype
七、二维数组
二维数组本质上是「数组的数组」,常用于表示矩阵、表格等结构:
javascript
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
// 访问元素
console.log(matrix[0][0]); // 1
console.log(matrix[1][2]); // 6
创建二维数组的坑
使用 fill 创建二维数组时要小心:
javascript
// 错误方式
const wrong = new Array(3).fill([]);
wrong[0].push(1);
console.log(wrong); // [[1], [1], [1]] - 三个数组共享同一个引用!
// 正确方式
const correct = Array.from({ length: 3 }, () => []);
correct[0].push(1);
console.log(correct); // [[1], [], []] - 互不影响
八、综合实战
8.1 数组去重
javascript
const arr = [1, 2, 2, 3, 3, 3, 4, 5, 5];
// 方式一:Set
const unique1 = [...new Set(arr)];
// 方式二:filter
const unique2 = arr.filter((item, index) => arr.indexOf(item) === index);
// 方式三:reduce
const unique3 = arr.reduce((pre, cur) =>
pre.includes(cur) ? pre : [...pre, cur], []);
console.log(unique1); // [1, 2, 3, 4, 5]
8.2 数组扁平化
javascript
const nested = [1, [2, [3, [4, [5]]]]];
// 方式一:flat
const flat1 = nested.flat(Infinity);
// 方式二:reduce
function flatten(arr) {
return arr.reduce((pre, cur) =>
Array.isArray(cur) ? [...pre, ...flatten(cur)] : [...pre, cur], []);
}
console.log(flatten(nested)); // [1, 2, 3, 4, 5]
8.3 统计元素出现次数
javascript
const arr = ['a', 'b', 'a', 'c', 'b', 'a'];
const count = arr.reduce((pre, cur) => {
pre[cur] = (pre[cur] || 0) + 1;
return pre;
}, {});
console.log(count); // { a: 3, b: 2, c: 1 }
九、总结
JavaScript 数组是前端开发中最重要的数据结构之一。本文涵盖的核心知识点:
- ADT 抽象数据类型:连续存储空间 + 特定操作
- 增删方法 :
push、pop、unshift、shift - 遍历方法 :
for、for...of、forEach - 高阶方法 :
map、filter、every、some、reduce - 原型链 :理解
Array.prototype的作用 - 二维数组:矩阵结构与常见坑点
熟练掌握这些内容,不仅能应对面试考察,更能在实际开发中写出简洁、高效的代码。
参考资料