for...in 和 for...of:用法、区别
1. for...in
循环
语法
for (const key in object) {// 循环体
}
用途
- 遍历对象的可枚举属性:
for...in
主要用于遍历对象的可枚举属性(包括继承自原型链的属性,除非被显式屏蔽)。 - 不推荐用于数组:虽然可以遍历数组的索引,但可能遇到以下问题:
- 遍历顺序不保证(ES6+ 中按数字升序,但非数字键可能不按顺序)。
- 可能遍历到非预期的原型链属性(需用
hasOwnProperty
过滤)。
示例
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {console.log(key, obj[key]); // 输出:a 1, b 2, c 3
}// 过滤原型链属性
const parent = { inheritedProp: 'foo' };
const child = Object.create(parent);
child.ownProp = 'bar';
for (const key in child) {if (child.hasOwnProperty(key)) {console.log(key, child[key]); // 输出:ownProp bar}
}
注意事项
- 原型链属性:默认会遍历原型链上的可枚举属性,需用
hasOwnProperty
过滤。 - 性能:在大型对象上可能比
Object.keys()
+forEach
慢。
2. for...of
循环
语法
for (const item of iterable) {// 循环体
}
用途
- 遍历可迭代对象:
for...of
用于遍历实现了[Symbol.iterator]
方法的可迭代对象(如数组、字符串、Map
、Set
、NodeList
等)。 - 直接获取值:每次迭代返回的是值(而非键或索引)。
示例
// 遍历数组
const arr = [10, 20, 30];
for (const num of arr) {console.log(num); // 输出:10, 20, 30
}// 遍历字符串
const str = 'hello';
for (const char of str) {console.log(char); // 输出:h, e, l, l, o
}// 遍历 Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {console.log(key, value); // 输出:a 1, b 2
}
注意事项
- 不可迭代对象:普通对象默认不可迭代,需手动实现
[Symbol.iterator]
或使用Object.keys()
/Object.values()
转换。 - 性能:通常比
forEach
或for
循环更高效,尤其在大型数据集上。
3. for...in
vs for...of
核心区别
特性 | for...in | for...of |
---|---|---|
遍历目标 | 对象的可枚举属性(包括原型链) | 可迭代对象的值(如数组、字符串等) |
返回值 | 键(属性名) | 值(直接是元素值) |
适用场景 | 遍历对象属性 | 遍历数组、字符串、Map 、Set 等 |
原型链属性 | 默认包含(需过滤) | 不涉及原型链 |
性能 | 可能较慢(需处理原型链) | 通常更快 |
4. 使用场景建议
-
for...in
:- 需要遍历对象的属性名时(如动态检查对象结构)。
- 注意:避免用于数组,优先用
for...of
或Array.prototype.forEach
。
-
for...of
:- 遍历数组、字符串、
Map
、Set
等可迭代对象。 - 需要直接操作值而非索引/键时。
- 遍历数组、字符串、
5. 扩展:手动实现对象的可迭代性
若需用 for...of
遍历对象,可手动实现 [Symbol.iterator]
:
const obj = { a: 1, b: 2, c: 3 };
obj[Symbol.iterator] = function* () {for (const key in this) {if (this.hasOwnProperty(key)) {yield [key, this[key]]; // 返回键值对}}
};for (const [key, value] of obj) {console.log(key, value); // 输出:a 1, b 2, c 3
}
总结
for...in
:专注于对象属性的遍历,需警惕原型链污染。for...of
:专注于可迭代对象的值遍历,语法简洁且性能优异。
根据实际需求选择合适的循环结构,可以显著提升代码的可读性和效率。