数组(Array)是前端开发中最常用的数据结构之一,也是面试高频考点。本文将系统梳理数组的高级操作与常见面试题型,结合底层原理与实用代码,助你面试和实战双提升!
一、稀疏数组与空位
概念解析
- 稀疏数组:数组中存在“空位”(empty slot),即某些索引没有实际元素。
- 空位:不是
undefined,而是根本没有值。比如new Array(3)或[1, , 3]。
代码示例
const arr = [1, , 3];
console.log(arr.length); // 3
console.log(arr[1]); // undefined,但实际上是 empty slot
console.log(1 in arr); // false
遍历行为
forEach、map、filter、reduce等方法会跳过空位。for...in、for循环不会跳过空位。
arr.forEach((v, i) => console.log(i, v)); // 只输出 0 和 2
for (let i = 0; i < arr.length; i++) {
console.log(i, arr[i]); // 输出 0,1,2
}
面试延伸
- 如何将稀疏数组转为稠密数组?
可以用Array.from(arr)或arr.filter(() => true)。
二、类数组与转换
概念解析
- 类数组:拥有
length属性和按索引访问的对象,但没有数组原型上的方法。 - 常见于
arguments、NodeList、自定义对象等。
function foo() {
console.log(arguments); // 类数组
}
转换方法
Array.from(类数组)[...类数组](需可迭代)Array.prototype.slice.call(类数组)
function foo() {
const arr1 = Array.from(arguments);
const arr2 = [...arguments];
const arr3 = Array.prototype.slice.call(arguments);
}
面试延伸
- 类数组为什么要转换为数组?为了使用数组的高阶方法(如
map、filter)。 NodeList在老浏览器不是可迭代对象,只能用Array.prototype.slice.call。
三、高阶方法
常见高阶方法
map:返回新数组,元素经过函数处理filter:返回新数组,筛选符合条件的元素reduce:归并/累加,返回单一值some/every:部分/全部满足条件判断find/findIndex:查找元素/索引flat/flatMap:数组扁平化
代码示例
const arr = [1, 2, 3, 4];
const mapped = arr.map(x => x * 2); // [2,4,6,8]
const filtered = arr.filter(x => x % 2 === 0); // [2,4]
const sum = arr.reduce((acc, cur) => acc + cur, 0); // 10
const hasEven = arr.some(x => x % 2 === 0); // true
const allPositive = arr.every(x => x > 0); // true
const found = arr.find(x => x > 2); // 3
const idx = arr.findIndex(x => x === 3); // 2
面试延伸
reduce的妙用:实现去重、扁平化、分组等复杂操作。flat的深度参数,flatMap的一体化处理。
四、变异与非变异方法
概念解析
- 变异方法:会直接修改原数组
push、pop、shift、unshift、splice、sort、reverse
- 非变异方法:不会修改原数组,返回新数组
concat、slice、map、filter、reduce
代码示例
const arr = [1, 2, 3];
arr.push(4); // arr 变为 [1,2,3,4]
const newArr = arr.concat(5); // arr 不变,newArr 为 [1,2,3,4,5]
面试延伸
- 为什么 React/Vue 推荐用非变异方法?
因为直接修改原数组可能导致状态不可追踪,影响组件更新。
五、深拷贝与浅拷贝
概念解析
- 浅拷贝:只复制一层,嵌套对象仍然引用原对象
- 深拷贝:递归复制所有层级,互不影响
代码示例
const arr = [{a:1}, {b:2}];
const shallow = arr.slice();
shallow[0].a = 100;
console.log(arr[0].a); // 100,说明是浅拷贝
const deep = JSON.parse(JSON.stringify(arr));
deep[0].a = 200;
console.log(arr[0].a); // 100,说明是深拷贝
面试延伸
JSON.parse(JSON.stringify())有局限(如丢失函数、循环引用)。- 推荐用 lodash 的
_.cloneDeep。
六、数组常见算法
去重
const arr = [1,2,2,3];
const unique = [...new Set(arr)];
// 或
const unique2 = arr.filter((v,i,a) => a.indexOf(v) === i);
扁平化
const arr = [1, [2, [3, 4]]];
const flat1 = arr.flat(Infinity);
// 或递归
function flatten(arr) {
return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []);
}
分组
const arr = [
{type: 'A', value: 1},
{type: 'B', value: 2},
{type: 'A', value: 3}
];
const grouped = arr.reduce((acc, cur) => {
(acc[cur.type] = acc[cur.type] || []).push(cur);
return acc;
}, {});
// grouped: {A: [{...}, {...}], B: [{...}]}
面试延伸
- 能否手写 reduce 实现这些算法?
- 能否用一行代码实现扁平化、去重?
结语
数组的高级操作不仅是面试常考,更是日常开发的必备技能。建议大家多练习高阶方法、手写常见算法,并理解底层原理,这样才能在面试和实际项目中游刃有余!