前端面试高频:数组的高级操作与实战

147 阅读3分钟

数组(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

遍历行为

  • forEachmapfilterreduce 等方法会跳过空位。
  • for...infor 循环不会跳过空位。
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 属性和按索引访问的对象,但没有数组原型上的方法。
  • 常见于 argumentsNodeList、自定义对象等。
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);
}

面试延伸

  • 类数组为什么要转换为数组?为了使用数组的高阶方法(如 mapfilter)。
  • 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 的一体化处理。

四、变异与非变异方法

概念解析

  • 变异方法:会直接修改原数组
    • pushpopshiftunshiftsplicesortreverse
  • 非变异方法:不会修改原数组,返回新数组
    • concatslicemapfilterreduce

代码示例

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 实现这些算法?
  • 能否用一行代码实现扁平化、去重?

结语

数组的高级操作不仅是面试常考,更是日常开发的必备技能。建议大家多练习高阶方法、手写常见算法,并理解底层原理,这样才能在面试和实际项目中游刃有余!