JavaScript 数组
数组和对象并称 JS 两大核心数据结构。数组是有序、可索引的列表,用于存放一组数据:用户列表、商品列表、购物车……几乎每个网页都要处理数组。这一章我们系统掌握数组,重点学 map、filter、reduce 这三大"函数式"方法。
1. 数组基础
// 数组:有序的值列表,用 [] 创建
const fruits = ["apple", "banana", "cherry"];
// 访问元素(下标从 0 开始)
console.log(fruits[0]); // apple
console.log(fruits.length); // 3
console.log(fruits[fruits.length - 1]); // cherry(最后一个)
// 修改元素
fruits[1] = "blueberry";
console.log(fruits); // ["apple","blueberry","cherry"]
// JS 数组可以放任意类型(但通常同质)
const mixed = [1, "hi", true, null, { name: "小明" }];
// 检查是否是数组(不要用 typeof,会返回 "object")
console.log(Array.isArray(fruits)); // true
console.log(Array.isArray("hi")); // falseJS 的数组可以放任意类型(不像 Java/C 数组必须同质),长度可变(不像静态语言需要预先分配)。判断是否数组要用 Array.isArray(),typeof [] 返回的是 "object"——这是经典坑。
2. 增删改查
// 末尾增删
const arr = [1, 2, 3];
arr.push(4); // 末尾添加,返回新长度
console.log(arr); // [1,2,3,4]
arr.pop(); // 末尾删除,返回被删的值
console.log(arr); // [1,2,3]
// 开头增删
arr.unshift(0); // 开头添加
console.log(arr); // [0,1,2,3]
arr.shift(); // 开头删除
console.log(arr); // [1,2,3]
// 任意位置增删:splice(起始下标, 删除数量, ...要插入的元素)
const nums = [10, 20, 30, 40];
nums.splice(1, 1); // 从下标 1 删 1 个
console.log(nums); // [10,30,40]
nums.splice(1, 0, 99); // 从下标 1 删 0 个,插入 99
console.log(nums); // [10,99,30,40]
nums.splice(2, 1, "a", "b"); // 替换:删 1 个,加 2 个
console.log(nums); // [10,99,"a","b",40]记忆口诀:
push/pop:末尾进出(像栈)。unshift/shift:头部进出。splice:任意位置万能操作(增、删、改都行)。
shift 和 unshift 性能比 push/pop 差,因为要移动所有元素,大数组上慎用。
3. 遍历数组
// 1. for-of:遍历值
const arr = [10, 20, 30];
for (const item of arr) {
console.log(item); // 10 / 20 / 30
}
// 2. 经典 for:需要索引
for (let i = 0; i < arr.length; i++) {
console.log(i, arr[i]);
}
// 3. forEach:简单遍历(无返回值,不能 break)
arr.forEach((item, index) => {
console.log(index, item);
});
// 4. map:对每个元素做处理,返回新数组
const doubled = arr.map(n => n * 2);
console.log(doubled); // [20,40,60]
// 5. filter:筛选满足条件的元素
const big = arr.filter(n => n > 15);
console.log(big); // [20,30]
// 6. find:找第一个满足条件的元素
const first = arr.find(n => n > 15);
console.log(first); // 20
// 7. some / every:判断
console.log(arr.some(n => n > 25)); // true(有任一满足)
console.log(arr.every(n => n > 0)); // true(全部满足)实战中 map/filter/forEach 用得最多——它们让代码更声明式、更简洁。能用这些方法的,就别写手写 for 循环。
4. reduce:汇总神器
reduce 是数组方法里最强、也最容易把新手搞晕的。它的作用是把数组"压缩"成一个值(求和、求积、找最值、转对象等):
// reduce:把数组"汇总"成一个值
// 接收:回调(累计值, 当前元素) 和 初始值
// 求和
const nums = [1, 2, 3, 4];
const sum = nums.reduce((acc, n) => acc + n, 0);
console.log(sum); // 10
// 找最大值
const max = nums.reduce((m, n) => Math.max(m, n));
console.log(max); // 4
// 数组转对象
const users = [
{ id: 1, name: "小明" },
{ id: 2, name: "小红" }
];
const userMap = users.reduce((map, u) => {
map[u.id] = u.name;
return map;
}, {});
console.log(userMap);
// { 1: "小明", 2: "小红" }
// 统计元素出现次数
const words = ["apple", "banana", "apple", "cherry", "banana", "apple"];
const counts = words.reduce((acc, w) => {
acc[w] = (acc[w] || 0) + 1;
return acc;
}, {});
console.log(counts); // { apple: 3, banana: 2, cherry: 1 }关键理解:acc(accumulator)是累计值,每次回调返回的结果会成为下一次的 acc。第二个参数 0 是初始值,不传就用数组第一个元素。
5. 排序 sort
sort 是 JS 数组最大的坑之一——默认按字符串 Unicode 排序,数字排序完全错乱:
// sort():默认按"字符串 Unicode"排序,数字会出问题!
const nums = [10, 5, 1, 25, 100];
nums.sort();
console.log(nums); // [1, 10, 100, 25, 5] ⚠️ 完全错了
// 正确做法:传比较函数
nums.sort((a, b) => a - b); // 升序
console.log(nums); // [1, 5, 10, 25, 100]
nums.sort((a, b) => b - a); // 降序
console.log(nums); // [100, 25, 10, 5, 1]
// 对象数组排序
const users = [
{ name: "小明", age: 20 },
{ name: "小红", age: 22 },
{ name: "小刚", age: 19 }
];
users.sort((a, b) => a.age - b.age); // 按年龄升序
console.log(users[0].name); // 小刚
// reverse():反转数组
const arr = [1, 2, 3];
arr.reverse();
console.log(arr); // [3, 2, 1]记住:数字排序永远要传比较函数。另外 sort 会修改原数组,如果不想改原数组,先用展开运算符或 slice() 复制一份再排。
6. 其他常用方法
// 其他常用方法
const arr = [3, 1, 4, 1, 5, 9, 2, 6];
// slice(起始, 结束):截取片段(返回新数组,不改原数组)
console.log(arr.slice(2, 5)); // [4, 1, 5](不含 end)
console.log(arr.slice(-3)); // [9, 2, 6](负数从末尾算)
// concat():合并数组(返回新数组)
const merged = [1, 2].concat([3, 4]);
console.log(merged); // [1,2,3,4]
// 推荐用展开运算符
const merged2 = [...[1, 2], ...[3, 4]];
// includes():是否包含某值
console.log(arr.includes(5)); // true
// indexOf() / lastIndexOf():找下标
console.log(arr.indexOf(1)); // 1
console.log(arr.indexOf(99)); // -1(不存在)
// join():数组 → 字符串
console.log([1, 2, 3].join("-")); // "1-2-3"
// flat():数组扁平化
const nested = [1, [2, [3, 4]]];
console.log(nested.flat()); // [1, 2, [3, 4]](默认只展开 1 层)
console.log(nested.flat(Infinity)); // [1, 2, 3, 4](全部展开)slice vs splice 区别(极易混淆):
slice(start, end):不改原数组,返回新片段。slice 想到 "sli-ce" 中有 "c" 表示 "copy"。splice(start, count, ...items):直接改原数组,能增删改任意位置。
7. 数组的"可变"与"不可变"
JS 数组方法分两类:
- 修改原数组(mutator):
push、pop、shift、unshift、splice、sort、reverse、fill。 - 返回新数组/值(accessor):
map、filter、slice、concat、find、reduce、includes、join。
函数式编程/React 开发推崇不可变(immutable)——永远不改原数组,而是返回新数组。这样代码更可预测,也方便调试。习惯用 map/filter/... 替代 push/splice。
// ❌ 老风格:改原数组
const arr = [1, 2, 3];
arr.push(4);
// ✅ 函数式:不改原数组
const arr2 = [1, 2, 3];
const newArr = [...arr2, 4];
console.log(arr2); // [1,2,3](不变)
console.log(newArr); // [1,2,3,4]8. 类数组与解构
// 数组解构
const [first, second, third] = [10, 20, 30];
console.log(first, second, third); // 10 20 30
// 跳过中间元素
const [a, , c] = [1, 2, 3];
console.log(a, c); // 1 3
// 剩余元素
const [head, ...rest] = [1, 2, 3, 4];
console.log(head, rest); // 1 [2,3,4]
// 交换两个变量(超实用!)
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1
// 类数组转真数组(arguments、NodeList)
function test() {
const arr = [...arguments]; // 用展开
const arr2 = Array.from(arguments); // 用 Array.from
console.log(arr, arr2);
}
test(1, 2, 3);9. 实战:商品列表处理
const products = [
{ id: 1, name: "键盘", price: 199, stock: 10 },
{ id: 2, name: "鼠标", price: 89, stock: 0 },
{ id: 3, name: "显示器", price: 1599, stock: 5 },
{ id: 4, name: "耳机", price: 499, stock: 20 }
];
// 1. 找所有有库存的商品
const available = products.filter(p => p.stock > 0);
console.log(available.length); // 3
// 2. 提取所有商品名
const names = products.map(p => p.name);
console.log(names); // ["键盘","鼠标","显示器","耳机"]
// 3. 计算总库存价值
const totalValue = products.reduce((sum, p) => sum + p.price * p.stock, 0);
console.log(totalValue); // 199*10 + 0 + 1599*5 + 499*20 = 21480
// 4. 按价格排序,取前 3 贵
const top3 = [...products].sort((a, b) => b.price - a.price).slice(0, 3);
console.log(top3.map(p => p.name)); // ["显示器","耳机","键盘"]
// 5. 转成 id → product 的映射
const productMap = products.reduce((m, p) => (m[p.id] = p, m), {});
console.log(productMap[3].name); // 显示器这是真实业务里最常见的操作——接口返回一个数组,你要过滤、转换、汇总、查找、排序。熟练掌握这些方法,日常开发就游刃有余。
小结
- 数组用
[]创建,长度可变,能放任意类型。 - 末尾增删用
push/pop,任意位置用splice。 - 遍历用
forEach/map/filter/find/some/every/reduce。 - 数字排序必须传比较函数
(a,b) => a-b。 - 不可变风格:
...+map/filter,React/函数式首选。
下一篇我们看 JS 的字符串方法。
← 上一篇 JavaScript 对象
下一篇 JavaScript 字符串 →