两个对象。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}), {})
}),
{}
);
我把这里所有的答案都看了一遍,然后拼凑出了一个我自己的答案。现有的大多数答案都不是我想要的方式。
这对于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连接。