JavaScript 遍历对象与数组的方法详解:优缺点与最佳实践
·
JavaScript 遍历对象与数组的方法详解:优缺点与最佳实践
在 JavaScript 开发中,遍历对象和数组是日常编码中最常见的操作之一。然而,JavaScript 提供了多种遍历方式,每种方法都有其适用场景、性能特点和潜在陷阱。本文将系统梳理主流的遍历方法,分析它们的优缺点,并给出相应的最佳实践建议,帮助开发者写出更高效、更安全、更具可读性的代码。
一、数组遍历方法
1. for 循环(传统 for)
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
优点:
- 性能最优(尤其在大型数组中)。
- 支持
break和continue。 - 可精确控制索引(如倒序、跳步等)。
缺点:
- 代码冗长,可读性较差。
- 容易因边界错误导致 bug(如
<=误用)。
最佳实践:
- 对性能敏感或需要复杂控制逻辑时优先使用。
- 缓存
length值以避免重复计算(现代 JS 引擎已优化,但仍是良好习惯):
for (let i = 0, len = arr.length; i < len; i++) { ... }
2. for...of 循环(ES6+)
for (const item of arr) {
console.log(item);
}
优点:
- 语法简洁,语义清晰。
- 支持
break/continue/return。 - 可遍历所有可迭代对象(包括
Set、Map、字符串等)。
缺点:
- 无法直接获取索引(需配合
entries())。 - 不支持 IE 浏览器(需 Babel 转译)。
最佳实践:
- 适用于大多数现代项目,尤其是只需访问元素值的场景。
- 若需索引,使用解构:
for (const [index, item] of arr.entries()) {
console.log(index, item);
}
3. forEach
arr.forEach((item, index) => {
console.log(item, index);
});
优点:
- 函数式风格,代码简洁。
- 自动提供
item、index、array三个参数。
缺点:
- 无法中断循环(不能使用
break或return跳出整个循环)。 - 性能略低于
for循环(函数调用开销)。 - 不适合异步操作(除非使用
async/await包裹,但仍是串行执行)。
最佳实践:
- 用于简单的同步遍历,且不需要中途退出。
- 避免在
forEach中使用async函数处理异步逻辑(应改用for...of+await)。
4. map / filter / reduce 等高阶函数
const doubled = arr.map(x => x * 2);
const evens = arr.filter(x => x % 2 === 0);
const sum = arr.reduce((acc, x) => acc + x, 0);
优点:
- 函数式编程,链式调用,表达力强。
- 不修改原数组(纯函数),易于测试和维护。
缺点:
- 每次调用都会创建新数组(
map/filter),内存开销较大。 - 多次遍历(链式调用时)可能影响性能。
最佳实践:
- 用于数据转换、筛选、聚合等场景。
- 避免过度链式调用;若性能敏感,可考虑单次
for循环完成多任务。
二、对象遍历方法
1. for...in
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key, obj[key]);
}
}
优点:
- 可遍历对象自身及继承的可枚举属性。
- 语法简单。
缺点:
- 会遍历原型链上的可枚举属性(需配合
hasOwnProperty过滤)。 - 遍历顺序不保证(尽管现代引擎通常按插入顺序)。
- 无法遍历 Symbol 键。
最佳实践:
- 仅在明确需要包含继承属性时使用。
- 务必使用
hasOwnProperty检查,或改用Object.keys()。
2. Object.keys() + forEach / for...of
Object.keys(obj).forEach(key => {
console.log(key, obj[key]);
});
// 或
for (const key of Object.keys(obj)) {
console.log(key, obj[key]);
}
优点:
- 仅遍历对象自身的可枚举属性(不含原型链)。
- 返回数组,可与其他数组方法组合使用。
缺点:
- 仅包含字符串键,不包含 Symbol 键。
- 创建中间数组,内存开销略高。
最佳实践:
- 现代项目中遍历普通对象的首选方式。
- 若需同时处理字符串键和 Symbol 键,使用
Reflect.ownKeys()。
3. Object.entries() / Object.values()
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
优点:
- 直接获取键值对或值,无需
obj[key]访问。 - 与
for...of结合使用非常优雅。
缺点:
- 同样不包含 Symbol 键。
- 创建新数组,有内存开销。
最佳实践:
- 当需要同时使用 key 和 value 时,优先使用
Object.entries()。
4. Reflect.ownKeys()(包含 Symbol)
Reflect.ownKeys(obj).forEach(key => {
console.log(key, obj[key]);
});
优点:
- 返回对象自身的所有键(包括字符串和 Symbol)。
- 不遍历原型链。
缺点:
- 包含不可枚举属性(如
Object.defineProperty定义的)。 - 使用频率较低,需了解其行为。
最佳实践:
- 当对象可能包含 Symbol 键,且需要完整遍历时使用。
三、总结对比表
| 方法 | 适用类型 | 可中断 | 性能 | 是否含原型 | 是否含 Symbol | 推荐场景 |
|---|---|---|---|---|---|---|
for | 数组 | ✅ | ⭐⭐⭐⭐⭐ | - | - | 高性能、复杂控制 |
for...of | 数组/可迭代对象 | ✅ | ⭐⭐⭐⭐ | - | - | 现代项目通用遍历 |
forEach | 数组 | ❌ | ⭐⭐⭐ | - | - | 简单同步遍历 |
map/filter | 数组 | ❌ | ⭐⭐ | - | - | 数据转换/筛选 |
for...in | 对象 | ✅ | ⭐⭐ | ✅ | ❌ | 需要继承属性时 |
Object.keys() | 对象 | 取决于外层循环 | ⭐⭐⭐ | ❌ | ❌ | 普通对象遍历 |
Object.entries() | 对象 | 取决于外层循环 | ⭐⭐⭐ | ❌ | ❌ | 需要 key-value 对 |
Reflect.ownKeys() | 对象 | 取决于外层循环 | ⭐⭐ | ❌ | ✅ | 包含 Symbol 的完整遍历 |
四、通用最佳实践建议
- 优先使用语义清晰的方法:如
map表示映射,filter表示过滤。 - 避免在
forEach中处理异步逻辑:改用for...of+await。 - 遍历对象时默认使用
Object.keys()或Object.entries(),避免for...in的陷阱。 - 性能关键路径使用传统
for循环。 - 不要修改正在遍历的数组/对象结构(如在循环中
splice),可能导致跳过元素。
掌握这些遍历方法的本质差异和适用场景,能让你在面对不同需求时做出更合理的技术选型,写出既高效又可维护的 JavaScript 代码。
更多推荐


所有评论(0)