要让普通业务实体对象支持for...of遍历,核心是为其实现[symbol.iterator]方法,返回符合迭代器协议的对象(含next()方法,返回{value, done});需明确遍历目标(如属性值、键名、键值对或业务字段),推荐在类中用生成器函数定义,确保每次调用返回全新迭代器且不破坏封装性。

要让普通业务实体对象支持 for...of 遍历,核心是为该对象实现 [Symbol.iterator] 方法,返回一个符合迭代器协议的对象(即有 next() 方法,返回 { value, done } 形式的对象)。
明确迭代目标:决定遍历什么
业务对象通常不是天然可迭代的集合,需先明确“遍历时想拿到什么”。常见选择包括:
- 对象自身的可枚举属性值(类似
Object.values()) - 属性键名(类似
Object.keys()) - 键值对数组(类似
Object.entries()) - 业务语义上的特定字段(如订单对象只遍历商品项、用户对象只遍历角色列表)
在类中定义 Symbol.iterator 方法
推荐在类定义中直接部署该方法。它必须是生成器函数(function*)或返回迭代器对象的普通函数。使用生成器更简洁直观:
class User {
constructor(name, roles, email) {
this.name = name;
this.roles = Array.isArray(roles) ? roles : [];
this.email = email;
}
<p>*[Symbol.iterator]() {
// 示例:只遍历业务上关心的字段值
yield this.name;
yield this.email;
yield this.roles;
}
}</p>
这样就能直接用 for...of:
const user = new User('张三', ['admin', 'editor'], 'zhang@example.com');
for (const item of user) {
console.log(item); // '张三' → 'zhang@example.com' → ['admin','editor']
}
支持多种遍历策略(可选进阶)
若需按不同方式遍历(如有时要键、有时要值),可不直接在实例上定义 [Symbol.iterator],而是提供显式方法,并让默认迭代器保持语义清晰。例如:
class Order {
constructor(id, items, total) {
this.id = id;
this.items = items || [];
this.total = total;
}
<p>// 默认遍历:只暴露业务核心数据(如商品项)
<em>[Symbol.iterator]() {
yield</em> this.items; // 委托遍历 items 数组
}</p><p>// 显式方法供其他需求
keys() {
return Object.keys(this)[Symbol.iterator]();
}</p><p>entries() {
return Object.entries(this)[Symbol.iterator]();
}
}</p>
调用时:
const order = new Order('ORD-001', [{ name: '笔记本', qty: 2 }], 99.9);
for (const item of order) {
console.log(item); // { name: '笔记本', qty: 2 }
}
<p>// 如需遍历属性名,显式调用
for (const key of order.keys()) {
console.log(key); // 'id', 'items', 'total'
}</p>
注意兼容性与边界情况
实际使用中需留意几点:
-
[Symbol.iterator]是 ES6+ 特性,确保运行环境支持(现代浏览器和 Node.js ≥ 4 均支持) - 方法必须返回迭代器对象,不能直接返回数组或原始值;否则
for...of会报错is not iterable - 若对象含异步数据(如待加载的关联列表),
[Symbol.iterator]本身不能是async function*(那是AsyncIterator),此时应改用for await...of并实现[Symbol.asyncIterator] - 避免在迭代器中修改原对象状态,保证遍历过程纯净、可预测










