两个对象。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()相同。
有办法做到这一点吗?
用例:合并默认配置
如果我们以以下形式定义配置:
const defaultConf = {
prop1: 'config1',
prop2: 'config2'
}
我们可以这样定义更具体的配置:
const moreSpecificConf = {
...defaultConf,
prop3: 'config3'
}
但是如果这些配置包含嵌套结构,这种方法就不再适用了。
因此,我写了一个函数,它只合并{key: value,…}并替换其余的。
const isObject = (val) => val === Object(val);
const merge = (...objects) =>
objects.reduce(
(obj1, obj2) => ({
...obj1,
...obj2,
...Object.keys(obj2)
.filter((key) => key in obj1 && isObject(obj1[key]) && isObject(obj2[key]))
.map((key) => ({[key]: merge(obj1[key], obj2[key])}))
.reduce((n1, n2) => ({...n1, ...n2}), {})
}),
{}
);
这里是@Salakar的答案的一个不可变(不修改输入)版本。如果你在做函数式编程,这很有用。
export function isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item));
}
export default function mergeDeep(target, source) {
let output = Object.assign({}, target);
if (isObject(target) && isObject(source)) {
Object.keys(source).forEach(key => {
if (isObject(source[key])) {
if (!(key in target))
Object.assign(output, { [key]: source[key] });
else
output[key] = mergeDeep(target[key], source[key]);
} else {
Object.assign(output, { [key]: source[key] });
}
});
}
return output;
}