for和of是两个完全不同的东西,但又总被人搞混。
最直接的区别是:for...in 遍历的是对象的属性名(key),而 for...of 遍历的是可迭代对象的值(value)。
听起来很简单,但实际用起来,坑就来了。
先说 for...in,它就是为遍历普通对象设计的
想象一个简单的场景,你想看看一个对象里都有什么:
“`javascript
const person = {
name: ‘老王’,
age: 30,
gender: ‘male’
};
for (let key in person) {
console.log(key);
}
// 输出:
// ‘name’
// ‘age’
// ‘gender’
“`
你看,for...in 把 person 对象的所有属性名(key)都打印出来了。如果你想要拿到对应的值,也很简单,用方括号语法 person[key] 就行。
javascript
for (let key in person) {
console.log(key + ': ' + person[key]);
}
// 输出:
// 'name: 老王'
// 'age: 30'
// 'gender: male'
这看起来很完美,对吧?但是,for...in 有个大问题:它会遍历对象原型链上的可枚举属性。 这是个什么意思呢?
简单来说,JavaScript 中的对象都有一个“后台老板”,就是它的原型(prototype)。如果你给这个“后台老板”添加了属性,那么 for...in 在遍历小弟的时候,也会把老板的属性给带出来。
举个例子:
“`javascript
Object.prototype.sharedProperty = ‘这是所有对象共享的属性’;
const person = {
name: ‘老王’,
age: 30
};
for (let key in person) {
console.log(key);
}
// 输出:
// ‘name’
// ‘age’
// ‘sharedProperty’
“`
这就不是我们想要的结果了。在实际开发中,你可能无意间引入了一个库,它修改了 Object.prototype,然后你的 for...in 循环就开始出现意想不到的行为。
为了避免这个问题,通常需要加上 hasOwnProperty() 来检查这个属性是不是对象自身的,而不是从原型链上继承来的。
javascript
for (let key in person) {
if (person.hasOwnProperty(key)) {
console.log(key);
}
}
// 输出:
// 'name'
// 'age'
这样就安全多了。
绝对不要用 for...in 遍历数组
很多人刚开始学的时候,会觉得既然 for...in 能遍历对象,那数组也是对象,应该也行吧? 理论上可以,但千万别这么做。
原因有几个:
1. 它遍历的是索引,而且是字符串类型。 你拿到的 index 是 “0”, “1”, “2” 这样的字符串,而不是数字 0, 1, 2。如果你直接拿去做数学运算,可能会得到奇怪的结果。
2. 它不能保证顺序。 虽然大多数现代浏览器会按顺序遍历,但规范本身并没有保证这一点。对于数组来说,顺序极其重要。
3. 它会遍历到数组的非数字属性和原型链上的属性。 这和上面提到的对象问题一样,甚至更糟。数组也可能被添加自定义属性。
“`javascript
Array.prototype.customMethod = function() {};
const arr = [‘a’, ‘b’, ‘c’];
arr.customProperty = ‘这是一个自定义属性’;
for (let i in arr) {
console.log(i);
}
// 可能的输出 (顺序不保证):
// ‘0’
// ‘1’
// ‘2’
// ‘customProperty’
// ‘customMethod’
“`
看到没?连原型上的方法都出来了,这绝对是灾难。所以,记住一句话:for...in 是为普通对象准备的,不是为数组准备的。
再来看 for...of,现代 JavaScript 遍历的救星
for...of 是在 ES6 中引入的,它的出现就是为了解决 for...in 的那些问题,并提供一个统一的遍历接口。
它的核心是迭代器协议(Iterator Protocol)。 只要一个数据结构实现了这个协议,它就是“可迭代的”(iterable),也就可以用 for...of 来遍历。
常见的可迭代对象包括:
数组 (Array)
字符串 (String)
Map
Set
NodeList (浏览器环境中的 DOM 集合)
for...of 的语法非常简洁,而且它直接遍历的是值。
我们用 for...of 来遍历一个数组:
“`javascript
const arr = [‘a’, ‘b’, ‘c’];
for (let value of arr) {
console.log(value);
}
// 输出:
// ‘a’
// ‘b’
// ‘c’
“`
干净利落。没有多余的属性,没有索引类型的问题,顺序也是正确的。
遍历字符串也一样方便:
“`javascript
const str = “hello”;
for (let char of str) {
console.log(char);
}
// 输出:
// ‘h’
// ‘e’
// ‘l’
// ‘l’
// ‘o’
“`
但是,for...of 不能直接用在普通对象上
这是新手最容易犯的错。如果你尝试用 for...of 遍历一个普通对象,会直接报错。
“`javascript
const person = {
name: ‘老王’,
age: 30
};
for (let value of person) { // 这行会报错
console.log(value);
}
// Uncaught TypeError: person is not iterable
“`
原因很简单:普通对象默认没有实现迭代器协议。 for...of 不知道该如何从这个对象里一个一个地取值。
那如果你真的想用 for...of 的简洁语法来遍历对象呢?也不是不行,需要借助一些辅助方法,把对象转换成可迭代的结构。常用的有三个:
-
Object.keys(): 获取对象的键组成的数组。javascriptfor (let key of Object.keys(person)) {
console.log(key + ': ' + person[key]);
}
-
Object.values(): 获取对象的值组成的数组。javascriptfor (let value of Object.values(person)) {
console.log(value);
}
-
Object.entries(): 获取对象的键值对组成的数组,每个键值对是一个[key, value]形式的小数组。javascriptfor (let [key, value] of Object.entries(person)) {
console.log(key + ': ' + value);
}
我个人最推荐 Object.entries(),因为它同时提供了键和值,而且通过解构赋值 [key, value],代码非常清晰易读。
场景选择:什么时候用哪个?
现在逻辑就很清晰了。
- 当你需要遍历一个普通对象的属性时,使用
for...in。并且,为了保险起见,最好总是带上hasOwnProperty()的检查。 - 当你需要遍历数组、字符串、Map、Set 等可迭代数据结构的值时,用
for...of。这是最安全、最直接的方式。 - 如果你想用
for...of的方式来遍历普通对象,请先用Object.keys()、Object.values()或Object.entries()把它转换成一个数组。

本站部分图片和内容来自网友上传和分享,版权归原作者所有,如有侵权,请联系删除!若转载,请注明出处:https://www.rzedutec.com/p/63332/