重写一个对象的for...of方法

18 阅读2分钟

# JavaScript类型:关于类型,有哪些你不知道的细节?有感

首先看下文章怎么说的:

我们可以使用 Symbol.iterator 来自定义 for…of 在对象上的行为:

    var o = new Object

    o[Symbol.iterator] = function() {
        var v = 0
        return {
            next: function() {
                return { value: v++, done: v > 10 }
            }
        }        
    };

    for(var v of o) 
        console.log(v); // 0 1 2 3 ... 9

代码中我们定义了 iterator 之后,用 for(var v of o) 就可以调用这个函数,然后我们可以根据函数的行为,产生一个 for…of 的行为。

这里我们给对象 o 添加了 Symbol.iterator 属性,并且按照迭代器的要求定义了一个 0 到 10 的迭代器,之后我们就可以在 for of 中愉快地使用这个 o 对象啦。

在 JavaScript 中,for...of 循环需要一个迭代器(iterator)来遍历对象。迭代器是一个实现了特定协议的对象,该协议要求它必须提供一个 next 方法。next 方法返回一个对象,这个对象必须包含两个属性:valuedone

疑惑

为什么 return 是一个对象?

return 是一个对象是因为它需要遵循迭代器协议。next 方法返回的对象表示当前迭代步骤的状态和结果:

  • value: 当前迭代的值。
  • done: 一个布尔值,表示迭代是否完成。

MDN 迭代器

Generator.prototype.next()

除了 next 属性还可以有什么?

虽然 next 是迭代器的核心方法,但迭代器对象可以有其他属性或方法,不过这些额外的内容不会影响 for...of 循环的工作方式。以下是一些可能存在的额外属性或方法:

image.png

1. return 方法

return 方法会在迭代器被外部终止时被调用(例如通过 breakreturn 提前退出循环)。它允许迭代器清理资源或执行其他必要的操作

o[Symbol.iterator] = function() {
    var v = 0;
    return {
        next: function() {
            if (v > 10) {
                return { value: undefined, done: true };
            }
            return { value: v++, done: false };
        },
        return: function() {
            console.log("Iterator is being stopped");
            return { done: true }; // 结束迭代
        }
    };
};

for (var v of o) {
    console.log(v);
} // 输出 0 到 9
// 如果提前退出循环,会调用 return 方法        
2. throw 方法

throw 方法允许迭代器处理错误。如果在迭代过程中抛出异常,throw 方法会被调用。

o[Symbol.iterator] = function() {
    var v = 0;
    return {
        next: function() {
            if (v > 10) {
                return { value: undefined, done: true };
            }
            if (v === 5) throw new Error("Error at value 5");
            return { value: v++, done: false };
        },
        throw: function(err) {
            console.error("Caught error in iterator:", err.message);
            return { done: true }; // 结束迭代
        }
    };
};

try {
    for (var v of o) {
        console.log(v);
    }
} catch (e) {
    console.error(e.message);
}
// 输出 0 到 4 后会捕获错误并结束迭代

总结

  • next 方法是迭代器的核心,必须返回一个对象,包含 value 和 done 属性。
  • 迭代器对象可以有额外的属性或方法,如 return 和 throw,但这些不是必须的。
  • for...of 循环只依赖 next 方法来遍历对象,其他方法主要用于特殊场景(如错误处理或资源清理)。