JavaScript 数组高阶技巧:10 个实用方法从入门到进阶
数组是 JavaScript 中最常用的数据结构之一。除了基础的 push、pop、map、filter,还有很多高级技巧可以让代码更优雅、更高效。本文整理了 10 个实用数组操作,涵盖去重、扁平化、分组、树形转换等常见场景。
1. 数组去重的 3 种写法
初级写法
function unique(arr) {
const res = [];
for (let i = 0; i < arr.length; i++) {
if (res.indexOf(arr[i]) === -1) {
res.push(arr[i]);
}
}
return res;
}中级写法
function unique(arr) {
return [...new Set(arr)];
}高级写法(对象数组去重)
function uniqueBy(arr, key) {
return [...new Map(arr.map(item => [item[key], item])).values()];
}
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 1, name: 'Alice' } // 重复
];
console.log(uniqueBy(users, 'id'));
// [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]原理:Map 会保留相同键的最后一个值,配合 .values() 即可完成对象数组去重。
2. 扁平化数组有多简单
多层嵌套数组扁平化,一行代码即可搞定:
const nested = [1, [2, [3, [4, 5]]]];
// ES2019+ 直接用 flat()
console.log(nested.flat(Infinity)); // [1, 2, 3, 4, 5]
// 兼容旧浏览器的写法
console.log(JSON.parse(`[${nested}]`)); // [1, 2, 3, 4, 5]JSON 序列化方式将 [1,[2,[3]]] 转为字符串再转回数组,实现自动扁平化。
3. 数组分块的优雅写法
将大数组分成固定大小的块:
function chunk(arr, size) {
return Array.from(
{ length: Math.ceil(arr.length / size) },
(_, i) => arr.slice(i * size, (i + 1) * size)
);
}
console.log(chunk([1, 2, 3, 4, 5, 6], 2));
// [[1, 2], [3, 4], [5, 6]]Array.from 的第二个参数是映射函数,可同时进行索引计算和切片,比传统 for 循环简洁许多。
4. 快速判断数组包含指定项
简单值判断用 includes,对象数组判断用 some 或 every:
const arr = [1, 2, 3, 4, 5];
console.log(arr.includes(3)); // true
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
console.log(users.some(u => u.id === 2)); // true
console.log(users.every(u => u.id > 0)); // truesome:任意一个满足条件返回 true
every:所有项都满足条件返回 true
比 find + undefined 判断语义更清晰
5. 数组映射转对象超方便
将数组转为键值对对象:
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' }
];
const userMap = Object.fromEntries(users.map(u => [u.id, u]));
console.log(userMap);
// {
// 1: { id: 1, name: 'Alice' },
// 2: { id: 2, name: 'Bob' },
// 3: { id: 3, name: 'Charlie' }
// }
console.log(userMap[2]); // { id: 2, name: 'Bob' }Object.fromEntries 是 ES2019 新增的方法,与 Object.entries 互为逆操作,是数组转对象的神器。
6. 数组乱序算法(Fisher-Yates 洗牌)
function shuffle(arr) {
const res = [...arr];
for (let i = res.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[res[i], res[j]] = [res[j], res[i]];
}
return res;
}
console.log(shuffle([1, 2, 3, 4, 5]));
// 随机顺序:[3, 1, 5, 2, 4]解构赋值 [a, b] = [b, a] 交换元素,比传统临时变量方式更优雅。
7. 数组取交集和差集
const arr1 = [1, 2, 3, 4, 5];
const arr2 = [3, 4, 5, 6, 7];
// 交集
const intersection = [...new Set(arr1)].filter(x => arr2.includes(x));
console.log(intersection); // [3, 4, 5]
// 差集(arr1 有但 arr2 没有)
const difference = [...new Set(arr1)].filter(x => !arr2.includes(x));
console.log(difference); // [1, 2]
// 并集
const union = [...new Set([...arr1, ...arr2])];
console.log(union); // [1, 2, 3, 4, 5, 6, 7]核心思路:先用 Set 去重,再用 filter 筛选,配合 includes 判断是否存在。
8. 数组最大/最小值
const numbers = [12, 45, 7, 89, 32];
console.log(Math.max(...numbers)); // 89
console.log(Math.min(...numbers)); // 7
// 超大数组避免堆栈溢出
function getMax(arr) {
return arr.reduce((max, num) => num > max ? num : max, -Infinity);
}
const bigArray = new Array(100000).fill(0).map((_, i) => i);
console.log(getMax(bigArray)); // 99999注意:Math.max(...arr) 会展开数组,超大数组可能导致堆栈溢出,此时用 reduce 更安全。
9. 数组分组(GroupBy)
ES2024 已原生支持 Object.groupBy,在此之前可以手动实现:
const users = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 },
{ name: 'David', age: 30 }
];
// 手动实现
const groupBy = (arr, key) =>
arr.reduce((acc, item) => {
(acc[item[key]] ??= []).push(item);
return acc;
}, {});
console.log(groupBy(users, 'age'));
// {
// 25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
// 30: [{ name: 'Bob', age: 30 }, { name: 'David', age: 30 }]
// }
// ES2024 原生写法
const grouped = Object.groupBy(users, ({ age }) => age);??= 空值赋值运算符:只有当值为 null 或 undefined 时才赋值,比 ||= 更严谨。
10. 数组转树形结构
后端返回扁平数组,前端转成树形结构:
const list = [
{ id: 1, pid: 0, name: 'A' },
{ id: 2, pid: 1, name: 'A-1' },
{ id: 3, pid: 1, name: 'A-2' },
{ id: 4, pid: 2, name: 'A-1-1' },
{ id: 5, pid: 0, name: 'B' }
];
function buildTree(arr, pid = 0) {
return arr
.filter(item => item.pid === pid)
.map(item => ({
...item,
children: buildTree(arr, item.id)
}));
}
console.log(buildTree(list));
// [
// { id: 1, pid: 0, name: 'A', children: [
// { id: 2, pid: 1, name: 'A-1', children: [
// { id: 4, pid: 2, name: 'A-1-1', children: [] }
// ]},
// { id: 3, pid: 1, name: 'A-2', children: [] }
// ]},
// { id: 5, pid: 0, name: 'B', children: [] }
// ]递归加双重过滤:先筛选当前层级的节点,再递归构建子节点,经典而优雅的实现方式。
总结
这些数组技巧看似简单,但组合起来可以解决很多实际问题:
去重、分组、树形转换是数据处理的核心
Set、Map、Object.fromEntries 是现代 JavaScript 的神器
解构赋值、空值赋值让代码更简洁
用好数组,代码质量会提升一个台阶。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!