如何合并含相同嵌套属性的对象并保留所有属性?是否需用扩展运算符?
深度合并JavaScript嵌套对象的解决方案
你遇到的问题其实是浅合并 vs 深度合并的区别——Object.assign()和直接用扩展运算符{...obj1, ...obj2}都是浅合并,只会处理对象的第一层属性,嵌套的子对象会直接被后面的对象覆盖,而不是合并它们的属性。
为什么扩展运算符单独用不行?
先看个例子,用扩展运算符试试:
let obj1 = {'foo': 'bar', 'far': {'tst': 1}}; let obj2 = {'far': {'other': 'token'}}; const merged = {...obj1, ...obj2}; console.log(merged); // 输出:{"foo":"bar","far":{"other":"token"}}
结果和Object.assign()一样,嵌套的far被完全替换了,因为扩展运算符只复制第一层的键值对,对于far这个键,后面的obj2.far直接覆盖了obj1.far的引用。
那怎么实现你要的深度合并呢?有几种方案:
方案1:手动处理已知的嵌套结构
如果你的对象结构是固定的、嵌套层级不多,可以直接用扩展运算符逐层合并:
const merged = { ...obj1, // 复制第一层属性 far: { ...obj1.far, // 合并嵌套的far对象的属性 ...obj2.far } }; console.log(merged); // 输出:{"foo":"bar","far":{"tst":1,"other":"token"}}
这种方法简单直接,但缺点是如果嵌套层级深或者结构不确定,写起来就很麻烦。
方案2:写一个通用的深度合并函数
如果需要处理任意结构的嵌套对象,可以写一个递归的深度合并函数,自动处理所有层级的对象:
// 判断是否是纯对象(排除数组、null等) function isObject(item) { return item && typeof item === 'object' && !Array.isArray(item); } function deepMerge(target, ...sources) { // 如果没有源对象了,直接返回目标对象 if (!sources.length) return target; const currentSource = sources.shift(); // 如果目标和当前源都是对象,就递归合并 if (isObject(target) && isObject(currentSource)) { for (const key in currentSource) { if (isObject(currentSource[key])) { // 如果目标对象没有这个键,先初始化一个空对象 if (!target[key]) Object.assign(target, { [key]: {} }); // 递归合并子对象 deepMerge(target[key], currentSource[key]); } else { // 非对象属性直接覆盖赋值 Object.assign(target, { [key]: currentSource[key] }); } } } // 继续合并剩下的源对象 return deepMerge(target, ...sources); } // 使用示例 const obj1 = {'foo': 'bar', 'far': {'tst': 1}}; const obj2 = {'far': {'other': 'token'}}; const mergedObj = deepMerge({}, obj1, obj2); // 传入空对象作为目标,避免修改原对象 console.log(mergedObj); // 输出:{"foo":"bar","far":{"tst":1,"other":"token"}}
这个函数可以处理多层嵌套的对象,还可以扩展支持数组合并(如果需要的话),适合通用场景。
方案3:使用ES2023的Object.merge()(推荐,需注意兼容性)
ES2023标准引入了Object.merge()方法,它原生支持深度合并,完全符合你的需求:
const obj1 = {'foo': 'bar', 'far': {'tst': 1}}; const obj2 = {'far': {'other': 'token'}}; const mergedObj = Object.merge({}, obj1, obj2); console.log(mergedObj); // 输出:{"foo":"bar","far":{"tst":1,"other":"token"}}
不过要注意,这个API比较新,目前支持的环境包括:
- 现代浏览器(Chrome 118+、Firefox 119+、Safari 17.1+)
- Node.js 20.0.0+
如果你的运行环境支持,这是最简洁的解决方案。
总结一下:扩展运算符本身不能直接实现深度合并,但可以用它来辅助处理单层合并;如果要深度合并,要么手动处理嵌套结构,要么用递归函数,或者用原生的Object.merge()(环境允许的话)。
内容的提问来源于stack exchange,提问作者Moshe
相关产品推荐
相关产品推荐

