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

有办法做到这一点吗?


当前回答

Ramda是一个很好的javascript函数库,它有mergeDeepLeft和mergeDeepRight。这些方法都能解决这个问题。请在这里查看文档:https://ramdajs.com/docs/#mergeDeepLeft

对于问题中的具体例子,我们可以使用:

import { mergeDeepLeft } from 'ramda'
const x = { a: { a: 1 } }
const y = { a: { b: 1 } }
const z = mergeDeepLeft(x, y)) // {"a":{"a":1,"b":1}}

其他回答

我在加载缓存redux状态时遇到了这个问题。如果我只是加载缓存的状态,我会遇到错误的新应用程序版本与更新的状态结构。

前面已经提到过,lodash提供了merge函数,我使用了这个函数:

const currentInitialState = configureState().getState();
const mergedState = _.merge({}, currentInitialState, cachedState);
const store = configureState(mergedState);

这很简单,也很有效:

let item = {
    firstName: 'Jonnie',
    lastName: 'Walker',
    fullName: function fullName() {
            return 'Jonnie Walker';
    }
Object.assign(Object.create(item), item);

解释:

create()创建新对象。如果你传递参数给函数,它将创建你的对象与其他对象的原型。如果你在一个对象的原型上有任何函数它们会被传递给另一个对象的原型。

object .assign()合并两个对象并创建一个全新的对象,它们不再有引用。这个例子对我来说很好。

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

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

简单递归解

使用对象。条目,遍历其中一个对象。如果条目不存在,则添加该条目;如果条目是对象,则递归。

常量x = {a: {a: 1}} Const y = {a: {b: 1}} JSON.parse(JSON.stringify(y)) const mergeIntoZ = (firstObj, secondObj) => { Object.entries (firstObj) .forEach(([key, value]) => { if (secondObj[key] === undefined) { secondObj[key] = value } else if (typeof value === 'object') { mergeIntoZ (firstObj(关键),secondObj[主要]) } }) } mergeIntoZ (x, z) console.log (z)

这是一个廉价的深度合并,使用尽可能少的代码我能想到。当前一个属性存在时,每个源都会覆盖它。

const { keys } = Object;

const isObject = a => typeof a === "object" && !Array.isArray(a);
const merge = (a, b) =>
  isObject(a) && isObject(b)
    ? deepMerge(a, b)
    : isObject(a) && !isObject(b)
    ? a
    : b;

const coalesceByKey = source => (acc, key) =>
  (acc[key] && source[key]
    ? (acc[key] = merge(acc[key], source[key]))
    : (acc[key] = source[key])) && acc;

/**
 * Merge all sources into the target
 * overwriting primitive values in the the accumulated target as we go (if they already exist)
 * @param {*} target
 * @param  {...any} sources
 */
const deepMerge = (target, ...sources) =>
  sources.reduce(
    (acc, source) => keys(source).reduce(coalesceByKey(source), acc),
    target
  );

console.log(deepMerge({ a: 1 }, { a: 2 }));
console.log(deepMerge({ a: 1 }, { a: { b: 2 } }));
console.log(deepMerge({ a: { b: 2 } }, { a: 1 }));