JavaScript 中 `for...of` 和 `for...in` 循环的主要区别是什么?
时间: 2025-07-12 08:20:24 浏览: 10
<think>我们被要求解释JavaScript中`for...of`和`for...in`循环的主要区别。
回答将分为两部分:先解释两者的区别,然后给出相关问题的列表。
主要区别:
1. 遍历对象类型不同:
- `for...in` 用于遍历对象的可枚举属性(包括自身和继承的可枚举属性,但通常我们只关心自身属性,所以可能需要配合`hasOwnProperty`)。
- `for...of` 用于遍历可迭代对象(如Array, Map, Set, String, TypedArray, arguments等)的元素。
2. 遍历内容不同:
- `for...in` 遍历的是键(key),即属性名(字符串类型)。
- `for...of` 遍历的是值(value),即元素的值。
3. 使用对象不同:
- `for...in` 可以遍历任何对象(包括普通对象),但遍历的是属性名。
- `for...of` 只能遍历具有`Symbol.iterator`属性的可迭代对象,否则会抛出错误。
4. 性能考虑:
- 在遍历数组时,`for...of`通常比`for...in`性能更好,因为`for...in`会遍历整个原型链上的可枚举属性,而`for...of`只遍历数组元素。
5. 遍历顺序:
- `for...in` 循环的遍历顺序不一定按照属性的顺序(虽然现代浏览器大多按照定义顺序,但数字键会优先并按照数字顺序,字符串键按照定义顺序,但存在一些特殊情况)。
- `for...of` 按照可迭代对象定义的元素顺序遍历。
示例代码说明:
示例1:遍历数组
```javascript
let arr = [3, 5, 7];
arr.foo = 'hello';
// for...in 会遍历数组索引和可枚举的非索引属性(如foo)
for (let i in arr) {
console.log(i); // 输出: "0", "1", "2", "foo"
}
// for...of 只遍历数组元素的值
for (let i of arr) {
console.log(i); // 输出: 3, 5, 7
}
```
示例2:遍历字符串
```javascript
let str = "boo";
for (let i in str) {
console.log(i); // 输出: "0", "1", "2"
}
for (let i of str) {
console.log(i); // 输出: "b", "o", "o"
}
```
示例3:遍历普通对象(非可迭代对象)
```javascript
let obj = { a: 1, b: 2, c: 3 };
// 使用 for...in 可以
for (let key in obj) {
console.log(key); // 输出: a, b, c
}
// 使用 for...of 会报错,因为普通对象不是可迭代对象
try {
for (let val of obj) {
console.log(val);
}
} catch (e) {
console.log(e.message); // 输出: obj is not iterable
}
// 如何让普通对象可用for...of?可以自定义迭代器,或者使用Object.keys等
for (let key of Object.keys(obj)) {
console.log(key); // 输出: a, b, c
}
```
总结:
- 当需要遍历对象的属性时,使用`for...in`(注意配合`hasOwnProperty`过滤继承属性)。
- 当需要遍历数组、字符串等可迭代对象的值时,使用`for...of`。
注意:在ES6中,新增的集合类型(如Set、Map)都内置了迭代器,所以可以使用`for...of`,而`for...in`不适用于这些集合。
§§相关问题§§:
1. 如何让一个自定义对象支持`for...of`循环?
2. 在`for...in`循环中,如何避免遍历到继承的属性?
3. 除了数组和字符串,还有哪些内置对象支持`for...of`循环?
4. 在遍历数组时,为什么通常不推荐使用`for...in`?</think>### JavaScript 中 `for...of` 和 `for...in` 循环的主要区别
#### 🎯 核心区别总结:
| 特性 | `for...of` | `for...in` |
|--------------|--------------------------------|--------------------------------|
| **遍历目标** | 可迭代对象的**值** | 对象的**可枚举属性键名** |
| **适用对象** | 数组、字符串、Map、Set 等 | 普通对象、数组(索引) |
| **原型属性** | 不遍历原型链属性 | 遍历自身+原型链可枚举属性 |
| **顺序保证** | 严格按迭代器顺序 | 不保证顺序(尤其数字属性) |
| **Symbol属性** | 可遍历`Symbol.iterator`定义值 | 不遍历Symbol属性键 |
#### 🔍 详细解释与代码示例:
1. **遍历目标不同**
```javascript
const arr = ['a', 'b', 'c'];
// for...of 遍历值
for (const value of arr) {
console.log(value); // 输出: 'a', 'b', 'c'
}
// for...in 遍历键名
for (const key in arr) {
console.log(key); // 输出: '0', '1', '2'
}
```
2. **原型链处理不同**
```javascript
Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};
const iterable = [3, 5, 7];
iterable.foo = 'bar'; // 添加自定义属性
// for...in 遍历原型链属性
for (const key in iterable) {
console.log(key);
// 输出: '0', '1', '2', 'foo', 'arrCustom', 'objCustom'
}
// for...of 仅遍历自身值
for (const value of iterable) {
console.log(value); // 输出: 3, 5, 7
}
```
3. **数据类型支持不同**
```javascript
const obj = { a: 1, b: 2 };
// for...in 支持普通对象
for (const key in obj) {
console.log(key); // 输出: 'a', 'b'
}
// for...of 不支持普通对象
try {
for (const value of obj) {} // 抛出错误
} catch (e) {
console.log(e.message); // "obj is not iterable"
}
// 让对象支持迭代
obj[Symbol.iterator] = function* () {
for (const key in this) {
yield this[key];
}
};
for (const value of obj) {
console.log(value); // 输出: 1, 2
}
```
4. **遍历顺序差异**
```javascript
const mixedKeys = {
2: 'two',
1: 'one',
b: 'bee',
0: 'zero',
a: 'ay'
};
// for...in: 数字属性优先按数字顺序
for (const key in mixedKeys) {
console.log(key); // 输出: '0', '1', '2', 'b', 'a'
}
// for...of: 严格按迭代器定义顺序
for (const key of Object.keys(mixedKeys)) {
console.log(key); // 输出: '2', '1', 'b', '0', 'a'
}
```
5. **Symbol属性处理**
```javascript
const sym = Symbol('custom');
const objWithSymbol = {
[sym]: 'symbolValue',
regular: 'normalValue'
};
// for...in 忽略Symbol属性
for (const key in objWithSymbol) {
console.log(key); // 只输出: 'regular'
}
// for...of 可通过特殊方式访问Symbol值
for (const value of Object.getOwnPropertySymbols(objWithSymbol)) {
console.log(objWithSymbol[value]); // 输出: 'symbolValue'
}
```
#### ⚠️ 使用建议:
1. 遍历**数组元素值** → 优先使用 `for...of`
2. 遍历**对象属性键** → 使用 `for...in` + `hasOwnProperty` 检查
3. 遍历**Map/Set** → 只使用 `for...of`
4. 需要**中断循环** → 避免使用 `forEach()`,改用 `for...of`
#### 🔧 最佳实践代码:
```javascript
// 安全遍历对象属性
const obj = { a: 1, b: 2 };
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(`${key}: ${obj[key]}`);
}
}
// 高效遍历数组
const arr = [10, 20, 30];
for (const value of arr) {
if (value > 15) break; // 支持中断
console.log(value);
}
// 遍历Map
const map = new Map([['a', 1], ['b', 2]]);
for (const [key, value] of map) {
console.log(key, value);
}
```
阅读全文
相关推荐



















