两个对象。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 mergeDeep (target, source)  {
    if (typeof target == "object" && typeof source == "object") {
        for (const key in source) {
            if (source[key] === null && (target[key] === undefined || target[key] === null)) {
                target[key] = null;
            } else if (source[key] instanceof Array) {
                if (!target[key]) target[key] = [];
                //concatenate arrays
                target[key] = target[key].concat(source[key]);
            } else if (typeof source[key] == "object") {
                if (!target[key]) target[key] = {};
                this.mergeDeep(target[key], source[key]);
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}

其他回答

许多答案使用数十行代码,或者需要向项目添加一个新库,但如果您使用递归,这只是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])块,你就都设置好了。

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

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

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

我使用下面的短函数进行深度合并对象。 这对我来说很有效。 作者在这里完全解释了它是如何工作的。

/*!
 * Merge two or more objects together.
 * (c) 2017 Chris Ferdinandi, MIT License, https://gomakethings.com
 * @param   {Boolean}  deep     If true, do a deep (or recursive) merge [optional]
 * @param   {Object}   objects  The objects to merge together
 * @returns {Object}            Merged values of defaults and options
 * 
 * Use the function as follows:
 * let shallowMerge = extend(obj1, obj2);
 * let deepMerge = extend(true, obj1, obj2)
 */

var extend = function () {

    // Variables
    var extended = {};
    var deep = false;
    var i = 0;

    // Check if a deep merge
    if ( Object.prototype.toString.call( arguments[0] ) === '[object Boolean]' ) {
        deep = arguments[0];
        i++;
    }

    // Merge the object into the extended object
    var merge = function (obj) {
        for (var prop in obj) {
            if (obj.hasOwnProperty(prop)) {
                // If property is an object, merge properties
                if (deep && Object.prototype.toString.call(obj[prop]) === '[object Object]') {
                    extended[prop] = extend(extended[prop], obj[prop]);
                } else {
                    extended[prop] = obj[prop];
                }
            }
        }
    };

    // Loop through each object and conduct a merge
    for (; i < arguments.length; i++) {
        merge(arguments[i]);
    }

    return extended;

};

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

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

适用于对象和数组的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
  }
}