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

有办法做到这一点吗?


当前回答

这是我刚刚写的另一个支持数组的程序。它把它们连接起来。

function isObject(obj) {
    return obj !== null && typeof obj === 'object';
}


function isPlainObject(obj) {
    return isObject(obj) && (
        obj.constructor === Object  // obj = {}
        || obj.constructor === undefined // obj = Object.create(null)
    );
}

function mergeDeep(target, ...sources) {
    if (!sources.length) return target;
    const source = sources.shift();

    if(Array.isArray(target)) {
        if(Array.isArray(source)) {
            target.push(...source);
        } else {
            target.push(source);
        }
    } else if(isPlainObject(target)) {
        if(isPlainObject(source)) {
            for(let key of Object.keys(source)) {
                if(!target[key]) {
                    target[key] = source[key];
                } else {
                    mergeDeep(target[key], source[key]);
                }
            }
        } else {
            throw new Error(`Cannot merge object with non-object`);
        }
    } else {
        target = source;
    }

    return mergeDeep(target, ...sources);
};

其他回答

https://lodash.com/docs/4.17.15#defaultsDeep

注意:此方法会使源发生突变。

_.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
// => { 'a': { 'b': 2, 'c': 3 } }

我不喜欢现有的解决方案。所以,我开始写我自己的。

Object.prototype.merge = function(object) {
    for (const key in object) {
        if (object.hasOwnProperty(key)) {
            if (typeof this[key] === "object" && typeof object[key] === "object") {
                this[key].merge(object[key]);

                continue;
            }

            this[key] = object[key];
        }
    }

    return this;
}

我希望这能帮助那些努力理解正在发生的事情的人。我在这里看到了很多无意义的变量。

谢谢

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

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

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

我使用lodash:

import _ = require('lodash');
value = _.merge(value1, value2);

我用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
}