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

有办法做到这一点吗?


当前回答

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

其他回答

// copies all properties from source object to dest object recursively
export function recursivelyMoveProperties(source, dest) {
  for (const prop in source) {
    if (!source.hasOwnProperty(prop)) {
      continue;
    }

    if (source[prop] === null) {
      // property is null
      dest[prop] = source[prop];
      continue;
    }

    if (typeof source[prop] === 'object') {
      // if property is object let's dive into in
      if (Array.isArray(source[prop])) {
        dest[prop] = [];
      } else {
        if (!dest.hasOwnProperty(prop)
        || typeof dest[prop] !== 'object'
        || dest[prop] === null || Array.isArray(dest[prop])
        || !Object.keys(dest[prop]).length) {
          dest[prop] = {};
        }
      }
      recursivelyMoveProperties(source[prop], dest[prop]);
      continue;
    }

    // property is simple type: string, number, e.t.c
    dest[prop] = source[prop];
  }
  return dest;
}

单元测试:

describe('recursivelyMoveProperties', () => {
    it('should copy properties correctly', () => {
      const source: any = {
        propS1: 'str1',
        propS2: 'str2',
        propN1: 1,
        propN2: 2,
        propA1: [1, 2, 3],
        propA2: [],
        propB1: true,
        propB2: false,
        propU1: null,
        propU2: null,
        propD1: undefined,
        propD2: undefined,
        propO1: {
          subS1: 'sub11',
          subS2: 'sub12',
          subN1: 11,
          subN2: 12,
          subA1: [11, 12, 13],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
        propO2: {
          subS1: 'sub21',
          subS2: 'sub22',
          subN1: 21,
          subN2: 22,
          subA1: [21, 22, 23],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      };
      let dest: any = {
        propS2: 'str2',
        propS3: 'str3',
        propN2: -2,
        propN3: 3,
        propA2: [2, 2],
        propA3: [3, 2, 1],
        propB2: true,
        propB3: false,
        propU2: 'not null',
        propU3: null,
        propD2: 'defined',
        propD3: undefined,
        propO2: {
          subS2: 'inv22',
          subS3: 'sub23',
          subN2: -22,
          subN3: 23,
          subA2: [5, 5, 5],
          subA3: [31, 32, 33],
          subB2: false,
          subB3: true,
          subU2: 'not null --- ',
          subU3: null,
          subD2: ' not undefined ----',
          subD3: undefined,
        },
        propO3: {
          subS1: 'sub31',
          subS2: 'sub32',
          subN1: 31,
          subN2: 32,
          subA1: [31, 32, 33],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      };
      dest = recursivelyMoveProperties(source, dest);

      expect(dest).toEqual({
        propS1: 'str1',
        propS2: 'str2',
        propS3: 'str3',
        propN1: 1,
        propN2: 2,
        propN3: 3,
        propA1: [1, 2, 3],
        propA2: [],
        propA3: [3, 2, 1],
        propB1: true,
        propB2: false,
        propB3: false,
        propU1: null,
        propU2: null,
        propU3: null,
        propD1: undefined,
        propD2: undefined,
        propD3: undefined,
        propO1: {
          subS1: 'sub11',
          subS2: 'sub12',
          subN1: 11,
          subN2: 12,
          subA1: [11, 12, 13],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
        propO2: {
          subS1: 'sub21',
          subS2: 'sub22',
          subS3: 'sub23',
          subN1: 21,
          subN2: 22,
          subN3: 23,
          subA1: [21, 22, 23],
          subA2: [],
          subA3: [31, 32, 33],
          subB1: false,
          subB2: true,
          subB3: true,
          subU1: null,
          subU2: null,
          subU3: null,
          subD1: undefined,
          subD2: undefined,
          subD3: undefined,
        },
        propO3: {
          subS1: 'sub31',
          subS2: 'sub32',
          subN1: 31,
          subN2: 32,
          subA1: [31, 32, 33],
          subA2: [],
          subB1: false,
          subB2: true,
          subU1: null,
          subU2: null,
          subD1: undefined,
          subD2: undefined,
        },
      });
    });
  });

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

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;
}

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

谢谢

有一些维护良好的库已经做到了这一点。npm注册表中的一个例子是merge-deep

我在加载缓存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])块,你就都设置好了。