数组和集合对象✅
用过 Array 哪些方法
答案
方法名 | 作用简介 |
---|---|
push | 向数组末尾添加元素 |
pop | 移除并返回数组最后一个元素 |
shift | 移除并返回数组第一个元素 |
unshift | 向数组开头添加元素 |
concat | 合并两个或多个数组 |
join | 将数组元素连接成字符串 |
slice | 返回数组的片段 |
splice | 添加/删除/替换数组元素 |
forEach | 遍历数组每个元素 |
map | 返回新数组,元素经函数处理 |
filter | 返回符合条件的新数组 |
reduce | 累加器,数组归约为单个值 |
find | 查找第一个符合条件的元素 |
findIndex | 查找第一个符合条件元素的索引 |
every | 检查所有元素是否满足条件 |
some | 检查是否有元素满足条件 |
includes | 判断数组是否包含某元素 |
indexOf | 查找元素首次出现的索引 |
lastIndexOf | 查找元素最后出现的索引 |
sort | 对数组排序 |
reverse | 反转数组顺序 |
flat | 扁平化嵌套数组 |
flatMap | 映射并扁平化数组 |
什么是伪数组和数组的区别是什么如何转换
答案
伪数组(Array-like Object)是具有 length
属性和索引化元素(如 arguments
对象、DOM 集合)的对象,但它们不是真正的数组实例,因此不具备数组的内置方法(如 push
, pop
, slice
等)。
核心特征:
length
属性:表示元素的数量。- 索引访问:可以通过数字索引(如
obj[0]
)访问元素。 - 非数组实例:
Array.isArray(arrayLikeObject)
返回false
。 - 缺少数组方法:不能直接调用
forEach
,map
等数组方法。
转换为真实数组的方法:
-
Array.from()
: ES6 引入,是转换类数组对象或可迭代对象的首选方法。const arrayLike = { 0: 'a', 1: 'b', length: 2 }
const trueArray = Array.from(arrayLike) // ['a', 'b'] -
展开运算符 (
...
): ES6 引入,简洁易读,适用于可迭代对象。const nodeList = document.querySelectorAll('div')
const trueArray = [...nodeList] -
Array.prototype.slice.call()
: 经典方法,通过调用数组的slice
方法并将其this
指向类数组对象。const arrayLike = { 0: 'a', 1: 'b', length: 2 }
const trueArray = Array.prototype.slice.call(arrayLike) // ['a', 'b']
转换后,就可以在新的数组上使用所有标准的数组方法了。
Iterator 的概念与作用
答案
Iterator(迭代器)是一种为各种不同数据结构提供统一访问机制的接口。它的作用有三点:1)为各种数据结构提供统一、简便的访问接口;2)使数据结构的成员能够按某种次序排列;3)支持 for...of 等新的遍历命令。
迭代器对象通过 next()
方法依次返回集合成员,每次返回 { value, done }
,其中 done
表示遍历是否结束。
只要数据结构部署了 Symbol.iterator
方法,就可以用 for...of 遍历。
const arr = ['a', 'b', 'c']
const iter = arr[Symbol.iterator]()
console.log(iter.next()) // { value: 'a', done: false }
console.log(iter.next()) // { value: 'b', done: false }
console.log(iter.next()) // { value: 'c', done: false }
console.log(iter.next()) // { value: undefined, done: true }
答案解析
Iterator 让不同数据结构都能用统一方式遍历,极大提升了 JS 的可扩展性和一致性。
面试官视角
- 核心考察点:能否准确描述 Iterator 的作用和基本用法
- 评分标准:
- 差:只会用 for...of,不知其原理
- 良:能说出 next()、value、done
- 优:能举例说明并解释统一遍历机制
哪些数据结构原生支持 Iterator?如何自定义?
答案
原生支持 Iterator 的数据结构有:数组、字符串、Set、Map、类数组对象(如 arguments、NodeList)。
普通对象默认不支持,但可以通过实现 Symbol.iterator
方法自定义。
const obj = {
* [Symbol.iterator] () {
yield 1
yield 2
}
}
for (const v of obj) {
console.log(v) // 1, 2
}
答案解析
掌握哪些结构可直接 for...of,能自定义 Symbol.iterator 是进阶能力。
面试官视角
- 核心考察点:能否区分原生支持与自定义
- 评分标准:
- 差:只会遍历数组
- 良:能说出 Set/Map 支持
- 优:能写出自定义 iterator
Iterator 接口会在什么场合被自动调用?
答案
自动调用 Iterator 接口的场合有:
- for...of 循环
- 解构赋值(如
[a, b] = set
) - 扩展运算符(
...
) - yield* 表达式
- Array.from、Promise.all、Map/Set 构造等
const set = new Set(['a', 'b', 'c'])
const [x, y] = set
console.log(x, y) // a b
console.log([...set]) // ['a', 'b', 'c']
答案解析
理解这些场合有助于写出更简洁的代码。
面试官视角
- 核心考察点:能否列举常见自动调用场景
- 评分标准:
- 差:只知道 for...of
- 良:能说出解构、扩展运算符
- 优:能举例说明
Set 和 Map 的底层实现及时间复杂度?
答案
Set 和 Map 通常基于哈希表(HashMap)实现,查找、插入、删除操作平均时间复杂度 O(1)。
哈希冲突严重时最坏可退化为 O(n)。
答案解析
理解底层实现有助于合理选择数据结构和优化性能。
面试官视角
- 核心考察点:能否说出哈希表原理和复杂度
- 评分标准:
- 差:只会用不会分析
- 良:能说出 O(1)
- 优:能说明哈希冲突和最坏情况
Map 和 WeakMap、Set 和 WeakSet 有什么区别?
答案
- Map/Set:键可以是任意类型,强引用,支持遍历和 size。
- WeakMap/WeakSet:键只能是对象,弱引用,不可遍历,无 size。
- 弱引用:如果对象没有其他引用,垃圾回收时会自动移除 WeakMap/WeakSet 中的键值对,防止内存泄漏。
答案解析
WeakMap/WeakSet 适合做缓存、私有数据存储,避免内存泄漏。
面试官视角
- 核心考察点:能否说出弱引用和应用场景
- 评分标准:
- 差:只会用 Map/Set
- 良:能说出 WeakMap/WeakSet 特点
- 优:能举例说明弱引用用途
如何遍历 Map?有哪些常用 API?
答案
for...of
遍历键值对forEach
遍历keys()
、values()
、entries()
返回对应的迭代器- 扩展运算符
[...map]
转数组
const map = new Map([['a', 1], ['b', 2]])
for (const [k, v] of map) { console.log(k, v) }
map.forEach((v, k) => console.log(k, v))
console.log([...map.keys()]) // ['a', 'b']
答案解析
掌握多种遍历方式,灵活应对不同需求。
面试官视角
- 核心考察点:能否熟练遍历 Map
- 评分标准:
- 差:只会 for...of
- 良:能用 forEach/keys
- 优:能用扩展运算符
WeakMap/WeakSet 的典型应用场景?
答案
WeakMap/WeakSet 适合存储对象的私有数据、缓存、元数据等场景。
当对象被销毁时,相关数据会自动被垃圾回收,避免内存泄漏。
const wm = new WeakMap()
let obj = {}
wm.set(obj, 'private')
obj = null // 对象被销毁,WeakMap 自动清理
答案解析
理解弱引用的自动回收机制,能写出健壮的缓存和私有数据方案。
面试官视角
- 核心考察点:能否举例说明 WeakMap/WeakSet 用途
- 评分标准:
- 差:只会用 Map
- 良:能说出缓存用途
- 优:能结合内存管理说明