(JavaScript)for...in 和 for...of 的区别

102 阅读4分钟

for...in

起源:for...in 循环是JavaScript早期版本的一部分,它最早出现在ECMAScript 3(1999年)标准中。

  • 用于遍历对象的可枚举属性,包括对象的自身属性和继承属性。
  • 通常用于遍历对象的属性名,而不是属性值。
  • 可能会出现意外行为,因为它遍历整个原型链上的属性
  • 不适用于数组,因为它会遍历数组原型链上的属性
const obj = { a: 1, b: 2, c: 3 };
for (let key in obj) {
    console.log(key, obj[key]);
}

for...in 循环在 JavaScript 中存在一些问题,特别是在遍历对象属性时。以下是一些主要问题:

  1. 遍历继承属性:for...in 循环会遍历对象的原型链上的属性,而不仅仅是对象本身的属性。这意味着如果对象继承了属性,循环会包括这些属性,这可能会导致意外的行为。

  2. 无法保证属性顺序:for...in 循环不保证属性的遍历顺序,因为对象属性在 JavaScript 中是无序的。 这意味着你无法确定属性遍历的顺序,这可能导致问题,尤其是在需要特定顺序的情况下。

  3. 不适合数组:虽然你可以使用for...in遍历数组,但它通常不是遍历数组元素的首选方法。它不仅会遍历数组元素,还会遍历数组原型链上的属性,可能导致不符合预期的结果。

  4. 不适合遍历内置对象:对于内置对象(如数组、字符串、Map 和 Set 等),for...in 循环通常不是最佳选择,因为这些对象通常实现了更强大的遍历方式,如for...of 循环和迭代器。

因此,为了避免这些问题,尤其是在遍历对象时,建议使用for...of 循环或其他更合适的方式,以确保更可靠和可预测的遍历行为。

for...of

起源:for...of 循环首次出现在ECMAScript 6(ES2015)标准中,引入了迭代器协议,用于遍历可迭代对象。

"迭代器协议"(Iterator Protocol)是 ECMAScript 6(ES2015)引入的一项规范,它定义了一种方式来遍历可迭代对象的元素。迭代器协议定义了两个主要方法:

  1. next(): 迭代器对象必须实现的方法,用于获取下一个元素的值。它返回一个包含两个属性的对象:
    • value:表示迭代器返回的下一个元素的值。
    • done:一个布尔值,指示是否已经遍历到最后一个元素,如果是最后一个元素则为 true,否则为 false
  2. [Symbol.iterator](): 这是一个特殊的内置符号(Symbol),用于定义一个迭代器对象。它必须返回一个具有 next() 方法的对象。这允许可迭代对象在 for...of 循环或使用迭代器进行遍历时,让迭代器对象被正确地识别和使用。

迭代器协议的主要目的是为了提供一种通用的方式来遍历各种数据结构,包括数组、字符串、Set、Map等,以及自定义可迭代对象。这使得 JavaScript 中的遍历更加统一和易于使用,无论数据结构的内部实现如何,都可以使用相同的语法来进行遍历操作。

下面是一个简单的示例,演示如何使用迭代器协议来自定义可迭代对象:

const customIterable = {
    values: [1, 2, 3, 4],
    [Symbol.iterator]: function() {
        let index = 0;
        return {
            next: () => {
                if (index < this.values.length) {
                    return { value: this.values[index++], done: false };
                } else {
                    return { value: undefined, done: true };
                }
            }
        };
    }
};

for (const item of customIterable) {
    console.log(item);
}

这里,customIterable 对象实现了迭代器协议,定义了 [Symbol.iterator]() 方法和 next() 方法,以允许使用 for...of 循环来遍历它的值。

  • 用于遍历可迭代对象的元素值,如数组、字符串、Set、Map等。
  • 不会遍历对象的属性,只能用于迭代对象的值
  • 在ECMAScript 6之后的版本中,大多数内置对象都被设计为可迭代对象,使for...of更强大和通用。
const arr = [1, 2, 3, 4];
for (let value of arr) {
    console.log(value);
}

总结

  • for...in 用于遍历对象的属性名,包括继承的属性,存在一些潜在的问题。
  • for...of 用于遍历可迭代对象的元素值,更适用于数组、字符串、Map、Set等,避免了for...in的一些问题。
  • for...in 是较早版本的JavaScript的一部分,而for...of 是在ECMAScript 6中引入的新特性,更加现代和安全。