Skip to main content

多种遍历方式

JavaScript 遍历方式全解析:从基础到高级

JavaScript 提供了多种遍历数据结构的方式,每种方式都有其适用场景和性能特点。以下是对常见遍历方式的全面汇总,涵盖数组、对象、Map、Set 等结构。

一、数组遍历

1. for 循环(基础版)

const arr = [1, 2, 3];
for (let i = 0; i < arr.length; i++) {
console.log(arr[i]);
}
  • 特点:控制灵活,可中断、反向遍历。
  • 性能:适合大型数组(避免 length 重复计算)。

2. for...of 循环

for (const item of arr) {
console.log(item);
}
  • 特点:支持迭代器协议(如数组、字符串、Set、Map)。
  • 优势:语法简洁,可使用 breakcontinue

3. forEach 方法

arr.forEach((item, index) => {
console.log(item, index);
});
  • 特点:内置方法,自动处理索引和边界。
  • 限制:无法使用 breakreturn 提前终止。

4. for...in 循环

for (const key in arr) {
console.log(key, arr[key]); // key 为字符串类型
}
  • 注意:遍历可枚举属性(包括原型链和非数字键),不推荐用于数组。

5. map/filter/reduce 等高阶方法

const doubled = arr.map(x => x * 2);
const sum = arr.reduce((acc, x) => acc + x, 0);
  • 特点:声明式编程,代码简洁,支持链式调用。
  • 性能:略低于传统循环(创建中间数组)。

二、对象遍历

1. for...in 循环

const obj = { a: 1, b: 2 };
for (const key in obj) {
console.log(key, obj[key]);
}
  • 特点:遍历对象自身和继承的可枚举属性(不含 Symbol 键)。

2. Object.keys()

Object.keys(obj).forEach(key => {
console.log(key, obj[key]);
});
  • 特点:仅遍历对象自身的可枚举字符串键。

3. Object.entries()

for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
  • 优势:直接获取键值对,支持解构和 for...of

4. Object.getOwnPropertyNames()

Object.getOwnPropertyNames(obj).forEach(key => {
console.log(key, obj[key]);
});
  • 特点:包含不可枚举属性(如 Object.defineProperty 定义的属性)。

5. Reflect.ownKeys()

Reflect.ownKeys(obj).forEach(key => {
console.log(key, obj[key]);
});
  • 特点:包含所有键(字符串和 Symbol,可枚举和不可枚举)。

三、Map 和 Set 遍历

1. Map 遍历

const map = new Map([['a', 1], ['b', 2]]);

// 遍历键值对
for (const [key, value] of map) {
console.log(key, value);
}

// 仅遍历键/值
map.keys();
map.values();
map.entries();

// forEach
map.forEach((value, key) => {
console.log(key, value);
});

2. Set 遍历

const set = new Set([1, 2, 3]);

for (const item of set) {
console.log(item);
}

set.forEach(item => {
console.log(item);
});
  • 特点:元素唯一,遍历时按插入顺序。

四、异步遍历

1. for await...of 循环

async function fetchData() {
const asyncIterable = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};

for await (const num of asyncIterable) {
console.log(num);
}
}
  • 适用场景:遍历异步生成器或 Promise 数组。

五、性能对比与最佳实践

1. 性能排序(从快到慢)

  1. 传统 for 循环(手动控制索引)
  2. for...of 循环(现代语法,性能接近传统 for)
  3. forEach/map/filter(函数调用开销)
  4. for...in 循环(遍历原型链,性能最差)

2. 选择建议

  • 性能敏感场景:使用传统 forfor...of
  • 需要中断遍历:使用 forfor...ofsome/every
  • 函数式编程:使用 map/filter/reduce
  • 对象遍历:优先使用 Object.keys()Object.entries()

六、常见陷阱与注意事项

1. for...in 的陷阱

Array.prototype.customMethod = () => {};
const arr = [1, 2, 3];

for (const key in arr) {
console.log(key); // 输出: 0, 1, 2, customMethod
}
  • 解决方案:使用 hasOwnProperty 过滤原型链属性:
    for (const key in arr) {
    if (arr.hasOwnProperty(key)) {
    console.log(key);
    }
    }

2. 异步遍历的错误方式

// 错误:forEach 不等待 Promise
const promises = [Promise.resolve(1), Promise.resolve(2)];
promises.forEach(async promise => {
const result = await promise;
console.log(result); // 不会按顺序执行
});

// 正确:使用 for...of 或 Promise.all
for await (const promise of promises) {
console.log(await promise); // 按顺序执行
}

七、总结表格

遍历方式适用结构是否支持中断是否保留顺序性能特点
for 循环数组最快
for...of 循环可迭代对象
forEach数组、Map、Set中等
for...in 循环对象最慢
Object.keys()对象
map/filter数组中等
for await...of异步可迭代对象中等(异步)

根据具体场景选择合适的遍历方式,既能保证代码简洁性,又能优化性能。在现代 JavaScript 中,推荐优先使用 for...of 和高阶数组方法,仅在需要最大性能或特殊控制时使用传统 for 循环。