目录一、先给面试中的标准结论一句话总结二、最核心区别1. for...in 遍历的是“键名 / 属性名”2. for...of 遍历的是“值”三、适用范围不同for...in 适合遍历对象for...of 适合遍历可迭代对象遍历字符串遍历 Set遍历 Map四、是否遍历原型链上的属性for...in 会遍历对象自身和原型链上可枚举属性for...of 不会遍历原型链属性五、遍历数组时为什么不推荐 for...in原因1遍历出来的是索引不是值原因2可能遍历到自定义属性原因3可能遍历原型链上的可枚举属性所以结论遍历数组六、底层原理区别for...in 的本质for...of 的本质七、能不能中断循环for...offor...in八、和 forEach 对比时怎么说更加分九、面试中最标准的对比表十、面试怎么回答更精彩版本1简洁标准版版本2高分版十一、面试官继续追问时怎么答追问1为什么对象不能直接 for...of追问2遍历对象你更推荐什么追问3数组遍历为什么不推荐 for...in十二、实际开发建议遍历对象示例遍历数组十三、一句话收尾总结这是 JavaScript 面试里的高频基础题。很多人只会答一句for...in遍历键for...of遍历值这句话没错但太浅。如果你想答得更好最好从这几个层面展开遍历对象不同遍历结果不同适用数据类型不同是否会遍历原型链上的属性是否适合遍历数组底层原理for...of基于迭代器Iterator一、先给面试中的标准结论一句话总结for...in用于遍历对象的可枚举属性名keyfor...of用于遍历可迭代对象的值value二、最核心区别1.for...in遍历的是“键名 / 属性名”const obj { a: 1, b: 2 } for (const key in obj) { console.log(key) } // a // b如果是数组const arr [10, 20, 30] for (const key in arr) { console.log(key) } // 0 // 1 // 2这里输出的是索引而且索引本质上是字符串。2.for...of遍历的是“值”const arr [10, 20, 30] for (const value of arr) { console.log(value) } // 10 // 20 // 30三、适用范围不同这个是面试特别喜欢追问的点。for...in适合遍历对象const obj { name: Tom, age: 20 } for (const key in obj) { console.log(key, obj[key]) }因为对象本身没有默认的迭代器所以不能直接用for...of遍历普通对象。const obj { a: 1, b: 2 } for (const item of obj) { // 报错 }会报TypeError: obj is not iterablefor...of适合遍历可迭代对象可迭代对象包括ArrayStringMapSetargumentsNodeListGenerator 返回值任何实现了Symbol.iterator的对象例如遍历字符串for (const ch of hello) { console.log(ch) }遍历 Setconst set new Set([1, 2, 3]) for (const item of set) { console.log(item) }遍历 Mapconst map new Map([ [name, Tom], [age, 20] ]) for (const item of map) { console.log(item) } // [name, Tom] // [age, 20]四、是否遍历原型链上的属性这是区分两者非常重要的一点。for...in会遍历对象自身和原型链上可枚举属性const obj { a: 1 } Object.prototype.b 2 for (const key in obj) { console.log(key) } // a // b所以使用for...in时通常要配合if (obj.hasOwnProperty(key)) { // 只处理对象自身属性 }完整写法for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { console.log(key) } }for...of不会遍历原型链属性因为它不是遍历“属性名”而是按迭代器规则取值。五、遍历数组时为什么不推荐for...in这是面试常问点必须会说。原因1遍历出来的是索引不是值const arr [10, 20, 30] for (const i in arr) { console.log(i) // 0 1 2 }你还要再写arr[i]不如for...of直接。原因2可能遍历到自定义属性const arr [10, 20, 30] arr.name test for (const key in arr) { console.log(key) } // 0 // 1 // 2 // name这往往不是我们想要的结果。原因3可能遍历原型链上的可枚举属性Array.prototype.xxx hello const arr [1, 2, 3] for (const key in arr) { console.log(key) } // 0 // 1 // 2 // xxx所以结论遍历数组推荐for...of不推荐for...in六、底层原理区别这个点说出来就比较加分了。for...in的本质遍历对象中可枚举enumerable属性名。也就是说它更像是“枚举对象属性”。for...of的本质遍历一个对象的迭代器Iterator。只要一个对象实现了Symbol.iterator它就可以被for...of遍历。例如数组内部实现了迭代器所以可以const arr [1, 2, 3] const iterator arr[Symbol.iterator]() console.log(iterator.next()) // { value: 1, done: false }for...of本质上就是不断调用这个迭代器的next()。七、能不能中断循环这个也常被追问。for...of可以使用breakcontinuereturnfor (const item of [1, 2, 3, 4]) { if (item 3) break console.log(item) }for...in同样也可以使用breakcontinue所以在“可中断性”上两者都支持。这一点和forEach不同forEach不能直接break。八、和forEach对比时怎么说更加分很多面试官会顺着问那for...of和forEach呢你可以这样答for...of可以break / continueforEach不行for...of支持await适合串行异步forEach不适合处理异步流程控制例如async function test() { const arr [1, 2, 3] for (const item of arr) { await Promise.resolve(item) console.log(item) } }而forEach(async item {})往往不是按预期串行执行。这个属于额外加分点。九、面试中最标准的对比表对比项for...infor...of遍历内容属性名 / 键名值适用对象普通对象可迭代对象能否遍历数组能但不推荐能推荐能否遍历对象能普通对象不能直接遍历是否遍历原型链可枚举属性会不会底层机制枚举对象属性基于 Iterator 迭代器常见用途遍历对象 key遍历数组、字符串、Set、Map十、面试怎么回答更精彩下面给你几个版本。版本1简洁标准版for...in和for...of最大的区别是遍历内容不同。for...in遍历的是对象的属性名适合遍历对象for...of遍历的是可迭代对象的值适合遍历数组、字符串、Set、Map 等。另外for...in会遍历原型链上的可枚举属性所以遍历对象时通常要配合hasOwnProperty过滤遍历数组时也不推荐用for...in因为它拿到的是索引还可能遍历到自定义属性。for...of底层基于迭代器Iterator更适合遍历数组和值类型集合。版本2高分版我一般会从“遍历内容、适用范围、原型链影响和底层机制”几个角度区分for...in和for...of。第一for...in遍历的是属性名 key本质上是在枚举对象的可枚举属性for...of遍历的是值 value。第二for...in更适合遍历普通对象for...of适合遍历实现了Symbol.iterator的可迭代对象比如数组、字符串、Set、Map。普通对象因为默认没有迭代器所以不能直接用for...of。第三for...in会遍历对象自身以及原型链上可枚举属性所以使用时通常需要配合hasOwnProperty做过滤而for...of不会有这个问题。第四遍历数组时通常推荐for...of不推荐for...in因为for...in拿到的是索引而且可能把数组上自定义属性也遍历出来。所以实际开发里我一般是遍历对象用for...in或Object.keys()遍历数组和可迭代对象优先用for...of。十一、面试官继续追问时怎么答追问1为什么对象不能直接for...of因为for...of依赖对象实现Symbol.iterator接口而普通对象默认不是可迭代对象所以不能直接用for...of。如果要遍历对象可以这样const obj { a: 1, b: 2 } for (const key of Object.keys(obj)) { console.log(key, obj[key]) }追问2遍历对象你更推荐什么这是一个很实战的问题。你可以答如果只是遍历对象自身属性我更倾向于Object.keys(obj)配合for...of因为这样不会碰到原型链上的可枚举属性代码也更安全直观。例如for (const key of Object.keys(obj)) { console.log(key, obj[key]) }这比直接for...in更稳。追问3数组遍历为什么不推荐for...in因为它遍历的是索引不是值而且会枚举数组上自定义属性和原型链上的可枚举属性容易产生意外结果所以数组遍历一般用for...of、普通for或forEach。十二、实际开发建议遍历对象推荐Object.keys(obj) Object.entries(obj) for...in hasOwnProperty示例const obj { a: 1, b: 2 } for (const [key, value] of Object.entries(obj)) { console.log(key, value) }遍历数组推荐for...of for forEach map/filter/reduce如果需要索引和值同时拿到const arr [a, b, c] for (const [index, value] of arr.entries()) { console.log(index, value) }十三、一句话收尾总结你可以最后这样总结面试里很好用for...in更偏向“遍历对象属性”for...of更偏向“遍历可迭代对象的值”。实际开发里我一般对象优先用Object.keys/entries配合循环数组优先用for...of这样可读性和安全性会更好。