两个对象。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 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);
您可以将其转换为函数(而不是构造函数)。
我知道这是一个老问题,但在ES2015/ES6中我能想到的最简单的解决方案实际上很简单,使用Object.assign(),
希望这能有所帮助:
/**
* Simple object check.
* @param item
* @returns {boolean}
*/
export function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
/**
* Deep merge two objects.
* @param target
* @param ...sources
*/
export function mergeDeep(target, ...sources) {
if (!sources.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
if (isObject(source[key])) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], source[key]);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
使用示例:
mergeDeep(this, { a: { b: { c: 123 } } });
// or
const merged = mergeDeep({a: 1}, { b : { c: { d: { e: 12345}}}});
console.dir(merged); // { a: 1, b: { c: { d: [Object] } } }
你将在下面的答案中找到一个不可更改的版本。
注意,这将导致循环引用上的无限递归。这里有一些关于如何检测循环引用的很好的答案,如果你认为你会面临这个问题。
function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
const isArray = Array.isArray;
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 (isPlainObject(source) || isArray(source)) {
for (const key in source) {
if (isPlainObject(source[key]) || isArray(source[key])) {
if (isPlainObject(source[key]) && !isPlainObject(target[key])) {
target[key] = {};
}else if (isArray(source[key]) && !isArray(target[key])) {
target[key] = [];
}
mergeDeep(target[key], source[key]);
} else if (source[key] !== undefined && source[key] !== '') {
target[key] = source[key];
}
}
}
return mergeDeep(target, ...sources);
}
// test...
var source = {b:333};
var source2 = {c:32, arr: [33,11]}
var n = mergeDeep({a:33}, source, source2);
source2.arr[1] = 22;
console.log(n.arr); // out: [33, 11]