两个对象。assign和Object spread只做浅合并。

这个问题的一个例子:

// No object nesting
const x = { a: 1 }
const y = { b: 1 }
const z = { ...x, ...y } // { a: 1, b: 1 }

输出是您所期望的。然而,如果我尝试这样做:

// Object nesting
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }

而不是

{ a: { a: 1, b: 1 } }

你得到

{ a: { b: 1 } }

X被完全覆盖,因为扩展语法只覆盖了一层。这与Object.assign()相同。

有办法做到这一点吗?


当前回答

Ramda是一个很好的javascript函数库,它有mergeDeepLeft和mergeDeepRight。这些方法都能解决这个问题。请在这里查看文档:https://ramdajs.com/docs/#mergeDeepLeft

对于问题中的具体例子,我们可以使用:

import { mergeDeepLeft } from 'ramda'
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = mergeDeepLeft(x, y)) // {"a":{"a":1,"b":1}}

其他回答

有办法做到这一点吗?

如果npm库可以作为一个解决方案,你的object-merge-advanced允许深度合并对象,并使用一个熟悉的回调函数定制/覆盖每一个合并操作。它的主要思想不仅仅是深度合并——当两个键相同时,值会发生什么变化?这个库负责处理这个问题——当两个键冲突时,object-merge-advanced会对类型进行加权,目的是在合并后保留尽可能多的数据:

第一个输入参数的键标记为#1,第二个参数的键标记为- #2。根据每种类型,将为结果键的值选择一个类型。在图表中,“对象”指的是普通对象(不是数组等)。

当键不冲突时,它们都输入结果。

在你的示例代码片段中,如果你使用object-merge-advanced来合并你的代码片段:

const mergeObj = require("object-merge-advanced");
const x = { a: { a: 1 } };
const y = { a: { b: 1 } };
const res = console.log(mergeObj(x, y));
// => res = {
//      a: {
//        a: 1,
//        b: 1
//      }
//    }

它的算法递归遍历所有输入对象键,比较和构建并返回新的合并结果。

适用于对象和数组的Vanilla Script解决方案:

const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = { ...x, ...y } // { a: { b: 1 } }

function deepmerge() {
  merge = function () {
    let target = arguments[0];
    for (let i = 1; i < arguments.length ; i++) {
      let arr = arguments[i];
            for (let k in arr) {
         if (Array.isArray(arr[k])) {
            if (target[k] === undefined) {            
                 target[k] = [];
            }            
            target[k] = [...new Set(target[k].concat(...arr[k]))];
         } else if (typeof arr[k] === 'object') {
            if (target[k] === undefined) {            
                 target[k] = {};
            }
            target[k] = merge(target[k], arr[k]);
         } else {
              target[k] = arr[k];         
         }
      }
    }
    return target;
  }
  return merge(...arguments);
}
console.log(deepmerge(x,y));

输出:

{
  a: {
    a: 1,
    b: 1
  }
}

我在加载缓存redux状态时遇到了这个问题。如果我只是加载缓存的状态,我会遇到错误的新应用程序版本与更新的状态结构。

前面已经提到过,lodash提供了merge函数,我使用了这个函数:

const currentInitialState = configureState().getState();
const mergedState = _.merge({}, currentInitialState, cachedState);
const store = configureState(mergedState);

许多答案使用数十行代码,或者需要向项目添加一个新库,但如果您使用递归,这只是4行代码。

函数合并(当前,更新){ for (Object.keys(updates)的key) { if (!current. hasownproperty (key) || typeof updates[key] !== 'object') current[key] = updates[key]; Else merge(current[key], updates[key]); } 返回当前; } console.log(合并({答:{:1}},{:{b: 1}}));

数组处理:上面的版本用新值覆盖旧的数组值。如果你想保留旧的数组值并添加新的,只需在else语句上方添加一个else If (current[key] instanceof array && updates[key] instanceof array) current[key] = current[key].concat(updates[key])块,你就都设置好了。

如果你想要一个单行程序,而不需要像lodash那样庞大的库,我建议你使用deepmerge (npm install deepmerge)或deepmerge-ts (npm install deepmerge-ts)。

deepmerge也为TypeScript提供了类型,并且更加稳定(因为它比较老),但是deepmerge-ts也可用于Deno,并且从设计上看更快,尽管顾名思义是用TypeScript编写的。

一旦导入就可以了

deepmerge({ a: 1, b: 2, c: 3 }, { a: 2, d: 3 });

得到

{ a: 2, b: 2, c: 3, d: 3 }

这对于复杂的对象和数组非常有效。这是一个真正的全面解决方案。