为何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
相关产品推荐
相关产品推荐

