当前位置: 首页 > 知识库问答 >
问题:

如何进行深度合并而不是浅合并?

蒙光华
2023-03-14

Object.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()相同。

有办法做到这一点吗?

共有3个答案

马魁
2023-03-14

当涉及到宿主对象或比一包值更复杂的任何类型的对象时,这个问题是非常重要的

  • 是调用getter来获取值还是复制属性描述符?
  • 如果合并目标有一个setter(自己的属性或在它的原型链中)呢?您认为该值已经存在还是调用setter来更新当前值?
  • 您是调用自己的属性函数还是复制它们?如果它们是绑定函数或箭头函数,取决于定义它们时作用域链中的某些东西,那该怎么办?
  • 如果它是类似DOM节点的东西呢?你肯定不想把它当作简单的对象,只是将它的所有属性深度合并到
  • 如何处理简单的结构,如数组、地图或集合?考虑他们已经存在或合并他们太?
  • 如何处理不可枚举的自有属性?
  • 新的子树呢?简单地通过引用或深度克隆分配?
  • 如何处理冻结/密封/不可扩展的对象?

另一件需要记住的事情:包含循环的对象图。它通常不难处理——只需保留一组已经访问过的源对象的Set——但常常被遗忘。

您可能应该编写一个深度合并函数,该函数只期望原始值和简单对象(最多是结构化克隆算法可以处理的类型)作为合并源。如果遇到任何无法处理的问题,或只是通过引用而不是深度合并来分配,则抛出。

换言之,没有一种适合所有人的算法,您要么自己使用,要么寻找一种恰好涵盖您的用例的库方法

景正文
2023-03-14

您可以使用Lodash合并:

var object = {
  'a': [{ 'b': 2 }, { 'd': 4 }]
};

var other = {
  'a': [{ 'c': 3 }, { 'e': 5 }]
};

_.merge(object, other);
// => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
万英武
2023-03-14

我知道这是一个老问题,但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] } } }

你会在下面的答案中找到一个不可变的版本。

注意,这将导致循环引用上的无限递归。如果你认为你会面临这个问题,这里有一些关于如何检测循环引用的很好的答案。

 类似资料:
  • 问题内容: 无论Object.assign和对象传播只能做一浅合并。 问题的一个示例: 输出是您期望的。但是,如果我尝试这样做: 代替 你得到 x完全被覆盖,因为传播语法仅深入了一层。与相同。 有没有办法做到这一点? 问题答案: 有人知道ES6 / ES7规范中是否存在深度合并? 不,不是的。

  • 分支合并分为两种情况,一种是本地分支合并,一种是远程分支合并到本地分支,下面,分别用GIF动画演示 本地合并分支: 远程分支合并

  • 注意 由于JS对象包括的范围非常广,加上ES6又有众多的新特性,很难、也没必要做到囊括所有的类型和情况,这里说的"对象",指的是普通的对象,不包括修改对象原型链, 或者为"Function","Promise"等的情况,请留意。 在ES6中,我们可以很方便的使用Object.assign进行对象合并,但这只是浅层的合并,如果对象的属性为数组或者对象的时候,会导致属性内部的值丢失。 注意: 此处合并

  • 我目前正在迭代一个数组,其中每个索引包含两个节点和一个关系(第1部分)- 我读了这篇文章,但我不明白如何实现它,使两个相同的节点具有相同的ID。我现在的代码是这样的: 创建两个节点 创造他们的关系 将关系添加到节点 坚持使用Neo4jTemplate.save() 我需要更改什么以合并而不是创建?我是否需要在持久化之前进行检查,或者在持久化SDN 4时是否有方法进行检查? 编辑: 我决定使用Neo

  • 问题内容: 通常我会使用浅拷贝对象 这是一个例子: 将给我们: 但是,如果我想合并对象以使父键不会被子对象覆盖,该怎么办: 将给我们: 是否有一个Angular函数已经执行了我不知道的深度合并? 如果不是,则有一种本机方法可以在javascript中递归地执行n个级别的操作。 问题答案: Angular 1.4或更高版本 用途: 不同于,递归地进入源对象的对象属性,执行深层复制。 旧版本的Angu

  • 问题内容: Python中是否有一个可用于深度合并字典的库: 以下: 当我结合时,我希望它看起来像: 问题答案: 我希望我不会重新发明轮子,但是解决方案相当短。而且,超级有趣的代码。 因此,其想法是将源复制到目标,并且每次源中的命令都进行递归。因此,如果在A中给定元素包含字典而在B中包含任何其他类型,则确实存在错误。