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

有办法做到这一点吗?


当前回答

我把这里所有的答案都看了一遍,然后拼凑出了一个我自己的答案。现有的大多数答案都不是我想要的方式。

这对于2021年来说是相当可怕的,所以任何改善的建议,我都洗耳恭听!

这是在Typescript中

type Props = Record<string, any>

export const deepMerge = (target: Props, ...sources: Props[]): Props => {
  if (!sources.length) {
    return target
  }

  Object.entries(sources.shift() ?? []).forEach(([key, value]) => {
    if (!target[key]) {
      Object.assign(target, { [key]: {} })
    }

    if (
      value.constructor === Object ||
      (value.constructor === Array && value.find(v => v.constructor === Object))
    ) {
      deepMerge(target[key], value)
    } else if (value.constructor === Array) {
      Object.assign(target, {
        [key]: value.find(v => v.constructor === Array)
          ? target[key].concat(value)
          : [...new Set([...target[key], ...value])],
      })
    } else {
      Object.assign(target, { [key]: value })
    }
  })

  return target
}

平面数组使用[…]删除重复值。新的(…)]。

嵌套数组使用concat连接。

其他回答

如果您想合并多个普通对象(不要修改输入对象)。基于对象。分配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));

如果你想要一个单行程序,而不需要像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 }

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

你可以使用Lodash合并:

Var对象= { 'a': [{'b': 2}, {'d': 4}] }; Var other = { 'a': [{'c': 3}, {'e': 5}] }; console.log(_。合并(对象,其他)); / / = > {a: [{b: 2,“c”:3},{' d ': 4,“e”:5}]} < script src = " https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js " > < /脚本>

另一个使用递归的变体,希望你觉得有用。

const merge = (obj1, obj2) => {

    const recursiveMerge = (obj, entries) => {
         for (const [key, value] of entries) {
            if (typeof value === "object") {
               obj[key] = obj[key] ? {...obj[key]} : {};
               recursiveMerge(obj[key], Object.entries(value))
            else {
               obj[key] = value;
            }
          }

          return obj;
    }

    return recursiveMerge(obj1, Object.entries(obj2))
}

有时候你并不需要深度合并,即使你这样认为。例如,如果您有一个带有嵌套对象的默认配置,并且您希望用自己的配置对其进行深入扩展,您可以为此创建一个类。概念很简单:

function AjaxConfig(config) {

  // Default values + config

  Object.assign(this, {
    method: 'POST',
    contentType: 'text/plain'
  }, config);

  // Default values in nested objects

  this.headers = Object.assign({}, this.headers, { 
    'X-Requested-With': 'custom'
  });
}

// Define your config

var config = {
  url: 'https://google.com',
  headers: {
    'x-client-data': 'CI22yQEI'
  }
};

// Extend the default values with your own
var fullMergedConfig = new AjaxConfig(config);

// View in DevTools
console.log(fullMergedConfig);

您可以将其转换为函数(而不是构造函数)。