这是一个非常经典的 JavaScript 面试题,考察对循环机制和数据结构的理解。for...in
和 for...of
看似相似,但用途和行为有本质区别。
✅ 核心区别总结
特性 | for...in | for...of |
---|---|---|
设计目的 | 遍历对象的可枚举属性(键) | 遍历可迭代对象的值 |
适用对象 | 所有对象(包括数组、普通对象) | 仅限可迭代对象(Array, Map, Set, String, arguments, TypedArray 等) |
遍历内容 | 属性的 键名(字符串) | 可迭代对象的 值 |
原型链 | 遍历自身 + 原型链上的可枚举属性 | 仅遍历对象自身的可迭代数据(不包括原型) |
可迭代协议 | 不要求实现 Symbol.iterator | 要求实现 Symbol.iterator 接口 |
🔍 详细对比与示例
1. 遍历数组
const arr = [10, 20, 30];
// for...in:遍历的是索引(字符串类型)
for (let key in arr) {
console.log(key); // "0", "1", "2"
console.log(typeof key); // string
}
// for...of:遍历的是元素值
for (let value of arr) {
console.log(value); // 10, 20, 30
}
⚠️ 注意:
for...in
遍历数组时,索引是字符串,且可能受原型污染影响。
2. 遍历普通对象
const obj = { a: 1, b: 2 };
// for...in:✅ 可以遍历对象的键
for (let key in obj) {
console.log(key); // "a", "b"
console.log(obj[key]); // 1, 2
}
// for...of:❌ 报错,因为普通对象不是可迭代的
for (let value of obj) {
// TypeError: obj is not iterable
}
✅ 正确做法:用
Object.keys(obj)
、Object.values()
或Object.entries()
配合for...of
。
for (let [key, value] of Object.entries(obj)) {
console.log(key, value); // "a" 1, "b" 2
}
3. 遍历字符串
const str = "hi";
for (let char of str) {
console.log(char); // "h", "i"
}
for (let index in str) {
console.log(index); // "0", "1"
}
4. 原型链影响
Array.prototype.customMethod = function() {};
const arr = [1, 2, 3];
for (let key in arr) {
console.log(key); // "0", "1", "2", "customMethod" ❌
}
for (let value of arr) {
console.log(value); // 1, 2, 3 ✅(不受原型方法影响)
}
for...in
会遍历到原型链上的可枚举属性,容易出 bug。
✅ 使用建议
- ✅ 遍历数组/可迭代对象的值 → 优先使用
for...of
- ✅ 遍历对象的属性键 → 使用
for...in
(但注意hasOwnProperty
判断) - ✅ 遍历对象的值或键值对 → 推荐
Object.keys/values/entries()
+for...of
- ❌ 不要用
for...in
遍历数组的值(易出错)
💡 面试回答模板
for...in
主要用于遍历对象的可枚举属性名(键),包括原型链上的属性,适合遍历普通对象。而for...of
是 ES6 引入的,用于遍历实现了Symbol.iterator
接口的可迭代对象(如数组、Map、Set 等),直接获取的是元素的值。它不适用于普通对象,除非对象本身定义了迭代器。因此,
for...of
更安全、更直观,推荐在遍历数组或集合时使用。
THE END