两个对象。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()相同。

有办法做到这一点吗?


当前回答

这里,直走;

一个简单的解决方案,工作像Object。仅赋值deep,适用于数组,无需任何修改。

function deepAssign(target, ...sources) { for (source of sources) { for (let k in source) { let vs = source[k], vt = target[k] if (Object(vs) == vs && Object(vt) === vt) { target[k] = deepAssign(vt, vs) continue } target[k] = source[k] } } return target } x = { a: { a: 1 }, b: [1,2] } y = { a: { b: 1 }, b: [3] } z = { c: 3, b: [,,,4] } x = deepAssign(x, y, z) console.log(JSON.stringify(x) === JSON.stringify({ "a": { "a": 1, "b": 1 }, "b": [ 1, 2, null, 4 ], "c": 3 }))

编辑: 我在别的地方回答过一种深度比较两个对象的新方法。 该方法也可以用于深度合并。如果你想要植入,请留言 https://stackoverflow.com/a/71177790/1919821

其他回答

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

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

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

如果您想合并多个普通对象(不要修改输入对象)。基于对象。分配polyfill

function isPlainObject(a) { return (!!a) && (a.constructor === Object); } function merge(target) { let to = Object.assign({}, target); for (let index = 1; index < arguments.length; index++) { let nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (let nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { if (isPlainObject(to[nextKey]) && isPlainObject(nextSource[nextKey])) { to[nextKey] = merge(to[nextKey], nextSource[nextKey]); } else { to[nextKey] = nextSource[nextKey]; } } } } } return to; } // Usage var obj1 = { a: 1, b: { x: 2, y: { t: 3, u: 4 } }, c: "hi" }; var obj2 = { b: { x: 200, y: { u: 4000, v: 5000 } } }; var obj3 = { c: "hello" }; console.log("result", merge(obj1, obj2, obj3)); console.log("obj1", obj1); console.log("obj2", obj2); console.log("obj3", obj3);

如果你想合并有限的深度

function isPlainObject(a) { return (!!a) && (a.constructor === Object); } function merge(target) { let to = Object.assign({}, target); const hasDepth = arguments.length > 2 && typeof arguments[arguments.length - 1] === 'number'; const depth = hasDepth ? arguments[arguments.length - 1] : Infinity; const lastObjectIndex = hasDepth ? arguments.length - 2 : arguments.length - 1; for (let index = 1; index <= lastObjectIndex; index++) { let nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (let nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { if (depth > 0 && isPlainObject(to[nextKey]) && isPlainObject(nextSource[nextKey])) { to[nextKey] = merge(to[nextKey], nextSource[nextKey], depth - 1); } else { to[nextKey] = nextSource[nextKey]; } } } } } return to; } // Usage var obj1 = { a: 1, b: { x: 2, y: { t: 3, u: 4, z: {zzz: 100} } }, c: "hi" }; var obj2 = { b: { y: { u: 4000, v: 5000, z: {} } } }; var obj3 = { c: "hello" }; console.log('deep 0', merge(obj1, obj2, obj3, 0)); console.log('deep 1', merge(obj1, obj2, obj3, 1)); console.log('deep 2', merge(obj1, obj2, obj3, 2)); console.log('deep 2', merge(obj1, obj2, obj3, 4));

我想介绍一个相当简单的ES5替代方案。该函数获得2个参数——目标和源,必须为“object”类型。Target将是结果对象。Target保留其所有原始属性,但它们的值可能会被修改。

function deepMerge(target, source) {
if(typeof target !== 'object' || typeof source !== 'object') return false; // target or source or both ain't objects, merging doesn't make sense
for(var prop in source) {
  if(!source.hasOwnProperty(prop)) continue; // take into consideration only object's own properties.
  if(prop in target) { // handling merging of two properties with equal names
    if(typeof target[prop] !== 'object') {
      target[prop] = source[prop];
    } else {
      if(typeof source[prop] !== 'object') {
        target[prop] = source[prop];
      } else {
        if(target[prop].concat && source[prop].concat) { // two arrays get concatenated
          target[prop] = target[prop].concat(source[prop]);
        } else { // two objects get merged recursively
          target[prop] = deepMerge(target[prop], source[prop]); 
        } 
      }  
    }
  } else { // new properties get added to target
    target[prop] = source[prop]; 
  }
}
return target;
}

例:

如果target没有source属性,则target获取source属性; 如果目标有source属性,而target & source没有 两个对象(4个中的3个),目标的属性被覆盖; 如果target确实有一个source属性,并且它们都是对象/数组(剩余1种情况),那么递归发生合并两个对象(或两个数组的连接);

还要考虑以下几点:

Array + obj = Array Obj + array = Obj Obj + Obj = Obj(递归合并) Array + Array = Array (concat)

它是可预测的,支持基本类型以及数组和对象。我们可以合并两个对象,我认为我们可以通过reduce函数合并两个以上的对象。

看一个例子(如果你想的话,也可以玩一下):

var a = { "a_prop": 1, "arr_prop": [4, 5, 6], "obj": { "a_prop": { "t_prop": 'test' }, "b_prop": 2 } }; var b = { "a_prop": 5, "arr_prop": [7, 8, 9], "b_prop": 15, "obj": { "a_prop": { "u_prop": false }, "b_prop": { "s_prop": null } } }; function deepMerge(target, source) { if(typeof target !== 'object' || typeof source !== 'object') return false; for(var prop in source) { if(!source.hasOwnProperty(prop)) continue; if(prop in target) { if(typeof target[prop] !== 'object') { target[prop] = source[prop]; } else { if(typeof source[prop] !== 'object') { target[prop] = source[prop]; } else { if(target[prop].concat && source[prop].concat) { target[prop] = target[prop].concat(source[prop]); } else { target[prop] = deepMerge(target[prop], source[prop]); } } } } else { target[prop] = source[prop]; } } return target; } console.log(deepMerge(a, b));

有一个限制-浏览器的调用堆栈长度。现代浏览器会在一些真正深层的递归中抛出错误(想想成千上万的嵌套调用)。此外,您还可以自由地处理像数组+对象等情况,因为您希望添加新的条件和类型检查。

我用es6做这个方法进行深度赋值。

function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item) && item !== null)
}

function deepAssign(...objs) {
    if (objs.length < 2) {
        throw new Error('Need two or more objects to merge')
    }

    const target = objs[0]
    for (let i = 1; i < objs.length; i++) {
        const source = objs[i]
        Object.keys(source).forEach(prop => {
            const value = source[prop]
            if (isObject(value)) {
                if (target.hasOwnProperty(prop) && isObject(target[prop])) {
                    target[prop] = deepAssign(target[prop], value)
                } else {
                    target[prop] = value
                }
            } else if (Array.isArray(value)) {
                if (target.hasOwnProperty(prop) && Array.isArray(target[prop])) {
                    const targetArray = target[prop]
                    value.forEach((sourceItem, itemIndex) => {
                        if (itemIndex < targetArray.length) {
                            const targetItem = targetArray[itemIndex]

                            if (Object.is(targetItem, sourceItem)) {
                                return
                            }

                            if (isObject(targetItem) && isObject(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else if (Array.isArray(targetItem) && Array.isArray(sourceItem)) {
                                targetArray[itemIndex] = deepAssign(targetItem, sourceItem)
                            } else {
                                targetArray[itemIndex] = sourceItem
                            }
                        } else {
                            targetArray.push(sourceItem)
                        }
                    })
                } else {
                    target[prop] = value
                }
            } else {
                target[prop] = value
            }
        })
    }

    return target
}

如果你正在使用ImmutableJS,你可以使用mergeDeep:

fromJS(options).mergeDeep(options2).toJS();