You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何JavaScript生成器返回的突变对象转数组时行为不一致?

为什么生成器手动调用next()和用Array.from转换结果不一致?

这个问题的核心原因其实很直白:JavaScript中的对象是引用类型,你的生成器每次yield的都是同一个bar对象的引用,而非每次生成一个独立的新对象副本。

手动调用next()的场景解析

当你依次调用baz.next().value并立刻执行console.log时,每一次next()都会让生成器执行到对应的yield语句——此时bar对象的状态是当前的(第一次是{a:0, b:0},第二次是{a:1, b:0},第三次是{a:1, b:10})。因为是同步执行逻辑,console.log会立即读取这个引用指向的对象的当前状态,所以结果完全符合预期。

Array.from的场景解析

Array.from的工作逻辑是一次性遍历完整个迭代器:它会连续调用三次next(),直到迭代器返回done: true。这意味着生成器内部的代码会被完整执行完毕——bar.a +=1和bar.b +=10都会生效,bar最终变成{a:1, b:10}。而数组里的三个元素,其实都是指向同一个bar对象的引用。当你console.log这个数组时,读取的是这三个引用指向的同一个对象的最终状态,所以三个元素看起来完全一致。

解决方法:返回对象副本而非引用

如果你想让Array.from得到每次yield时的对象状态,需要每次yield的时候返回一个新的对象副本,避免后续修改影响已存入数组的内容。可以用对象展开语法或者Object.assign实现:

function* foo() { 
  const bar = { a: 0, b: 0 }; 
  yield {...bar}; // 返回新对象副本
  bar.a += 1; 
  yield {...bar}; 
  bar.b += 10; 
  yield {...bar}; 
}

console.log(Array.from(foo())) // => [ { a: 0, b: 0 }, { a: 1, b: 0 }, { a: 1, b: 10 } ]

这样每次yield的都是独立的新对象,后续对bar的修改不会影响之前已经存入数组的对象,结果就和手动调用next()完全一致了。

内容的提问来源于stack exchange,提问作者krismath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:07:37