JavaScript如何使用generator生成器构造键值对映射对象
问题:使用generator构造普通键值对对象的简洁方案
目标是实现和generator构造数组一致的开发体验,不需要在generator内部提前构造完整结果对象,直接通过迭代产出内容、最后合并为普通键值对映射。
已验证的参考写法
构造数组时可以直接通过展开运算符消费generator:
function* rangeA(start, stop) { while(start < stop) yield start++ } let data = [...rangeA(1, 3), ...rangeA(20, 22)] // 输出: [1, 2, 20, 21]
非generator模式下构造对象的写法如下,但需要在函数内部维护临时结果对象:
function rangeB(start, stop) { let result = {} while(start < stop) { result[start] = start start++ } return result } let data = {...rangeB(1, 3), ...rangeB(20, 22)} // 输出: {1: 1, 2: 2, 20: 20, 21: 21}
直接yield单属性对象再展开generator的写法无效,先转数组再reduce合并的写法过于冗余:
// 无效写法 function* rangeC(start, stop) { while(start < stop) { yield {[start]: start} start++ } } let data = {...rangeC(1, 3), ...rangeC(20, 22)} // 输出: 空对象 {} let data2 = [...rangeC(1, 3), ...rangeC(20, 22)] // 输出: [{1: 1}, {2: 2}, {20:20}, {21:21}],不符合预期 let data3 = data2.reduce((a, b) => ({...a, ...b})) // 能得到正确结果,但写法冗余
回答
失效原因
对象展开运算符...只会拷贝操作数自身的可枚举属性,generator返回的是迭代器对象,本身不会把yield出来的内容挂载为自有属性,因此直接展开只能得到空对象。
最优方案:使用Object.fromEntries配合键值对格式的yield
这是原生支持、代码最简洁、性能最好的方案,和数组构造的逻辑完全对齐:
- 调整generator的yield内容为
[key, value]格式的二元数组 - 所有generator的迭代结果可以先像数组一样展开合并,再直接传入
Object.fromEntries转成普通对象
function* rangeC(start, stop) { while(start < stop) { // 产出 [键, 值] 格式的键值对 yield [start, start] start++ } } // 一行代码合并多个generator产出的内容为对象 const data = Object.fromEntries([ ...rangeC(1, 3), ...rangeC(20, 22) ]) // 输出: {1: 1, 2: 2, 20: 20, 21: 21}
Object.fromEntries是ES2019标准化的原生API,天生支持消费任何可迭代对象(包括generator返回的迭代器),不需要额外引入工具函数,语义就是「把键值对列表转为对象」,完全匹配使用场景。
可选封装:对齐对象展开的书写体验
如果你希望保持和{...a, ...b}完全一致的书写风格,可以封装一个1行的转换工具:
// 把迭代器转为普通对象 const iterToObj = it => Object.fromEntries(it) // 使用方式和普通对象展开完全一致 const data = { ...iterToObj(rangeC(1, 3)), ...iterToObj(rangeC(20, 22)) }
如果需要频繁合并多个generator产出的键值对,还可以封装一个支持多参数的合并函数,省去手动展开的步骤:
const mergeIter = (...iterables) => Object.fromEntries(function* () { for (const it of iterables) yield* it }()) // 直接传入多个generator调用结果即可 const data = mergeIter(rangeC(1,3), rangeC(20,22))
不推荐写法说明
之前尝试的「yield单属性对象 -> 转数组 -> reduce合并」的方案存在两个明显问题:
- 迭代过程中会创建大量临时的单属性对象,内存开销更高
- 多了一次reduce遍历,代码冗余,语义不直观
性能和可读性都远低于直接使用Object.fromEntries的方案。
内容的提问来源于stack exchange,提问作者j123b567
相关产品推荐
相关产品推荐

