克隆含自定义 Symbol.iterator 的对象时,Symbol.iterator 不会自动保留,需手动重挂载或基于原型复用;structuredClone 虽支持 symbol 键但函数体丢失,故必须确保克隆后该属性仍为返回新迭代器的函数。

克隆一个包含自定义 Symbol.iterator 的对象时,关键不是复制迭代逻辑本身,而是确保克隆后的对象仍具备完整的可迭代能力——即 [Symbol.iterator] 方法存在且能正常返回有效迭代器。
克隆后 Symbol.iterator 是否自动保留?
不会自动保留。因为 Symbol.iterator 是一个不可枚举、不可继承的自有属性(除非显式挂载在原型上)。使用浅克隆(如 Object.assign、展开运算符)或深克隆(如 structuredClone、第三方库)时,Symbol 键默认被忽略:
-
Object.assign({}, obj)和{...obj}:跳过所有 symbol 键 -
JSON.stringify()/JSON.parse():完全丢失 symbol 及其方法 -
structuredClone()(ES2022+):支持 symbol 键的克隆,但仅限于可序列化的 symbol 值;而Symbol.iterator是内置 symbol,克隆后其键存在,但原方法体是函数——函数无法被 structuredClone 序列化,所以结果中该属性值为undefined
安全克隆的两种推荐方式
要让克隆体真正可迭代,必须主动重建 [Symbol.iterator] 行为:
-
手动重挂载:克隆基础数据后,把原对象的
[Symbol.iterator]方法重新赋值给新对象
适用场景:克隆的是简单类数组或配置对象,迭代逻辑不依赖闭包状态 -
基于原型复用:将
[Symbol.iterator]定义在构造函数的prototype上,克隆实例时保持原型链完整
适用场景:多个实例共享同一套遍历规则,比如自定义 Range、LazyList 类
注意闭包状态是否需要隔离
如果原 [Symbol.iterator] 内部使用闭包维护了独立状态(如索引 let i = 0),那么每次调用都应生成全新迭代器——这点在克隆后依然成立。但如果你错误地把迭代器对象本身(而非生成函数)作为属性克隆,就会导致多次 for...of 共享同一状态,出现不可预期的跳值或提前结束。
正确做法始终是:克隆后,[Symbol.iterator] 仍是一个函数,每次调用返回一个新迭代器对象(或新生成器实例)。
实际操作建议
- 避免直接克隆带
Symbol.iterator的普通对象,优先封装为类 - 若必须克隆字面量对象,用
Object.assign+ 显式赋值:cloned[Symbol.iterator] = original[Symbol.iterator] - 对需深克隆的复杂结构,先提取可序列化数据(如
values、start/end),再用原始构造逻辑重建新实例










