数组是 JavaScript 最核心、使用频率最高的引用数据类型,几乎所有业务场景(数据渲染、数据筛选、格式处理、算法逻辑)都离不开数组操作。多数开发者只会基础增删改查,却不懂稀疏数组、数组空位差异、遍历底层区别、sort排序坑点、数组柯里化等面试高频、生产易踩坑知识点。
本文系统性全覆盖你要求的所有内容:Array对象完整能力、稀疏数组、数组空位、全类型遍历方式、数组排序原理与优化、数组柯里化,拆解底层原理、区分易错点、附实战代码与面试标准答案。
一、Array 对象核心详解
JavaScript 数组是可动态扩容的伪数组,本质是原型继承自 Array 对象的特殊对象,键为数字索引,自带海量原生操作方法。
1.1 数组创建方式
// 1. 字面量创建(推荐,性能最优)
const arr1 = [1,2,3]
// 2. Array 构造函数创建
const arr2 = new Array(1,2,3)
// 3. 传入单个数字:代表数组长度,而非数组元素(高频坑点)
const arr3 = new Array(5) // [empty × 5] 稀疏数组
1.2 Array 静态常用方法
-
Array.from():将伪数组、可迭代对象(字符串、Set、Map)转为真实数组,可接收第二个参数做遍历映射
-
Array.of():统一创建数组,解决 new Array() 单数字参数歧义,
Array\.of\(5\)=\[5\] -
Array.isArray():精准判断是否为数组,弥补 typeof 无法识别引用类型的缺陷
1.3 数组实例方法分类(必背)
1. 会改变原数组(变异方法)
push、pop、unshift、shift、splice、sort、reverse、fill
2. 不改变原数组(非变异方法)
map、filter、reduce、slice、concat、join、every、some、find、indexOf
3. 遍历迭代方法
forEach、for...of、entries、keys、values
二、稀疏数组 & 数组空位(面试超级重点)
这是前端面试最高频的易错点,绝大多数开发者混淆稀疏数组和空字符串/undefined 数组。
2.1 核心定义
-
数组空位(empty):数组当前索引不存在任何值,是真正的空,输出显示
empty -
稀疏数组:包含至少一个数组空位的数组,长度真实存在,但部分索引未定义
2.2 稀疏数组创建场景
// 场景1:new Array 指定长度
const arr1 = new Array(3) // [empty × 3]
// 场景2:字面量多余逗号
const arr2 = [1,,3] // [1, empty, 3]
// 场景3:删除数组元素
const arr3 = [1,2,3]
delete arr3[1]
console.log(arr3) // [1, empty, 3]
2.3 空位 vs undefined 本质区别
const emptyArr = [1,,3] // 稀疏数组,存在空位
const undefArr = [1,undefined,3] // 非稀疏数组,索引存在值为undefined
核心差异:空位是索引不存在,undefined 是索引存在、值为空,不同遍历方法对二者处理逻辑完全不同。
2.4 各方法对数组空位的处理规则(必背)
-
忽略空位(跳过遍历):forEach、map、filter、every、some、reduce
-
将空位转为 undefined:for...of、Array.from、扩展运算符 ...
-
保留空位:slice、concat
-
for循环:正常遍历,空位取值为 undefined
面试真题答案:想要抹平数组空位,统一用 Array\.from\(arr\) 或 \[\.\.\.arr\],可将所有 empty 转为 undefined。
三、数组全遍历方式详解与优劣对比
JS 数组遍历有 7 种主流方式,各自适配不同场景,存在能否中断、是否遍历空位、性能差异三大核心区别。
3.1 各类遍历特性汇总
| 遍历方式 | 能否中断(break/return) | 是否跳过空位 | 适用场景 |
|---|---|---|---|
| 普通 for 循环 | ✅ 可中断 | ❌ 不跳过(取undefined) | 高性能、需要中断遍历、复杂逻辑 |
| forEach | ❌ 不可中断 | ✅ 跳过空位 | 纯遍历、无需中断、简洁写法 |
| map | ❌ 不可中断 | ✅ 跳过空位 | 遍历并返回新数组、数据映射 |
| for...in | ✅ 可中断 | ✅ 跳过空位 | 遍历对象,禁止遍历数组(会遍历原型属性) |
| for...of | ✅ 可中断 | ❌ 转为undefined | 遍历可迭代对象、需要中断、兼容空位 |
3.2 核心避坑点
-
forEach 无法中断:即使写 return 也只能终止当前回调,无法跳出整个遍历
-
for...in 不适合数组:会遍历数组原型上的自定义属性,造成脏数据遍历
-
map 必须有返回值:无 return 会填充 undefined,造成新数组数据异常
四、数组排序(sort底层原理与实战优化)
Array.sort() 是数组排序核心方法,原生存在大量坑点,是面试算法高频考点。
4.1 原生 sort 默认规则(大坑)
默认情况下,sort 会将所有元素转为字符串,按照 Unicode 编码 排序,并非数字大小排序!
const arr = [10, 2, 30, 5]
console.log(arr.sort()) // [10,2,30,5] 排序错乱!
4.2 数字排序标准写法
const arr = [10, 2, 30, 5]
// 升序
arr.sort((a,b) => a - b) // [2,5,10,30]
// 降序
arr.sort((a,b) => b - a) // [30,10,5,2]
4.3 sort 底层排序算法
-
V8 引擎规则:数组长度 ≤ 22 采用插入排序(稳定、常数时间小);长度 > 22 采用快速排序(效率高、不稳定)
-
特性:sort 是变异方法,会直接修改原数组
4.4 复杂对象数组排序(实战常用)
const list = [{age:20}, {age:18}, {age:25}]
// 根据 age 升序排序
list.sort((a,b) => a.age - b.age)
五、数组柯里化(高阶实战+面试难点)
柯里化是前端高阶函数核心思想,数组柯里化指:将通用数组处理函数,拆分为多个单参数函数,实现参数复用、逻辑封装、延迟执行,大幅简化重复数组操作。
5.1 柯里化核心思想
多参数函数 → 拆分为多个单参数嵌套函数,参数逐步传入、逻辑最后执行。适配数组筛选、映射、去重等高频场景。
5.2 基础数组柯里化示例(筛选复用)
// 通用数组筛选柯里化函数
const filterArr = (key) => (arr) => {
return arr.filter(item => item.type === key)
}
// 参数复用:提前锁定筛选类型
filterTypeA = filterArr('A')
filterTypeB = filterArr('B')
// 直接传入数组即可使用,无需重复传type参数
const listA = filterTypeA(list)
const listB = filterTypeB(list)
5.3 高阶实战:数组映射柯里化
// 抽取数组指定字段(高频业务封装)
const mapField = (field) => (arr) => arr.map(item => item[field])
// 复用:快速抽取不同字段
const getNames = mapField('name')
const getIds = mapField('id')
const data = [{id:1,name:'张三'}, {id:2,name:'李四'}]
console.log(getNames(data)) // ['张三','李四']
5.4 数组柯里化核心优势
-
参数复用:固定通用参数,避免重复传参
-
逻辑解耦:通用逻辑封装,业务代码更简洁
-
延迟执行:先定义规则,后传入数据执行,适配动态业务场景
六、全文核心总结(面试必背)
-
Array对象:字面量创建最优,Array.from/of 解决创建与伪数组转换问题,区分变异/非变异方法
-
稀疏数组&空位:empty 是空索引,遍历多数方法会跳过,可用扩展运算符、Array.from 抹平空位
-
数组遍历:for 性能最优可中断,forEach/map 不可中断,for...of 兼容空位,禁止for...in遍历数组
-
数组排序:sort默认按Unicode排序,数字需传回调函数,V8采用插入+快排结合算法
-
数组柯里化:高阶函数封装,实现参数复用、逻辑复用,简化重复数组处理逻辑
七、高频面试简答题
-
稀疏数组和普通数组区别? 稀疏数组包含empty空位,索引不存在,多数遍历方法会跳过;普通数组每一位索引均有值(含undefined)。
-
如何解决数组空位问题? 使用扩展运算符、Array.from转换数组,将empty统一转为undefined,抹平空位差异。
-
forEach能不能中断遍历? 不能,仅能终止当前回调,无法跳出整个遍历,需要中断请使用for/for...of。
-
sort排序为什么结果错乱? 默认按字符串Unicode编码排序,数字排序必须传入(a,b)=>a-b/b-a回调函数。
-
数组柯里化的意义? 封装通用数组逻辑,实现参数复用、延迟执行,减少重复代码,提升代码可维护性。