如何在ES6+中合并两个javascript对象?

164

我厌倦了总是要写这样的代码:

function shallowExtend(obj1,obj2){
  var key;
  for ( key in obj2 ) {
    if ( obj2.hasOwnProperty(key) === false )  continue;
    obj1[key] = obj2[key]
  }
}

或者,如果我不想亲自编写代码,可以实现一个已经完成此功能的库。肯定ES6+正在解决这个问题,并为我们提供像Object.prototype.extend(obj2...)Object.extend(obj1,obj2...)这样的东西。

那么ES6+是否提供了这样的功能呢?如果还没有,那么是否计划添加这样的功能?如果没有计划,那么为什么?


3
那为什么你没有将它添加到你的图书馆中呢? - RobG
13
这个问题关于希望ES6能够让我们不再需要这样的样板代码。附带一提:https://github.com/balupton/bal-util/blob/aaade3e4f34e7590ba31fa55a0b6e1df044d10b8/src/lib/flow.coffee#L68-L91 - balupton
我并不认为有一种通用的方式可以将一个对象的名称/值对复制到另一个对象中。你只处理自己的属性还是处理[[Prototype]]链上的属性?你做的是“深拷贝”还是“浅拷贝”?那么非枚举和不可写属性呢?我认为,我更愿意使用一个小型库函数来实现我所需要的功能,而且大多数情况下这是可避免的。 - RobG
请参见如何动态合并两个JavaScript对象的属性?,以获取非ES6解决方案。 - Bergi
6个回答

220

76
小提示:Object.assign 会修改目标对象。如果这不是您想要的,可以使用一个空对象调用它:let merged = Object.assign({}, source1, source2); - david
21
请注意这里的扩展是浅层扩展,嵌套对象不会合并。 - monzonj
@monzonj,有没有不使用lodash或mout的选项来扩展嵌套对象? - Joao Falcao
1
有一种好的方法可以使用Object.assign而不需要使用库来深度合并嵌套对象:请查看我的答案 - Ruslan
一个扩展嵌套对象的旧方法是使用 JSON.parse(JSON.stringify(src)) - Andre Figueiredo

184
您可以使用对象展开语法来完成此操作:
const merged = {...obj1, ...obj2}

传递操作符对于数组已经是ES6(ES2015)的一部分,但对于对象则在ES9(ES2018)的语言规范中添加。它的提案早在此之前就已经默认启用了像Babel这样的工具。


3
现在官方称其为ES2015:P 自从ES6以来,解构不是其中的一部分吗?https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment使用babel-node运行: const ob1 = {foo: 123}; const ob2 = {bar: 234}; const merged = {...ob1, ...ob2}; console.log(merged)输出结果: { foo: 123, bar: 234 } - Thijs Koerselman
9
那不是解构,而是展开——对于对象来说,它不是 ES6 的一部分。你应该在 Babel 中禁用实验性的 ES7 草案。 - Bergi
2
啊,对不起。你是正确的。目前这是一个Babel阶段2的功能。https://github.com/sebmarkbage/ecmascript-rest-spread 我之前从来没有意识到这一点,因为我一直在使用babel,并且它默认启用了该功能。但由于你需要进行转译,而对象展开是一件非常简单的事情,所以我仍然建议使用它。我喜欢它。 - Thijs Koerselman
2
阶段为2或以上的提案默认启用。 - Thijs Koerselman
4
注意:当obj1obj2中存在相同的属性时,右侧(后者)对象的值优先。 - rinogo
显示剩余2条评论

17

我知道这是一个有点老的问题,但在ES2015/ES6中最简单的解决方案实际上非常简单,使用Object.assign()。

希望这可以帮到你,同时这还可以进行深度合并:

/**
 * Simple is object check.
 * @param item
 * @returns {boolean}
 */
export function isObject(item) {
  return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
}

/**
 * Deep merge two objects.
 * @param target
 * @param source
 */
export function mergeDeep(target, source) {
  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 target;
}

使用示例:

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] } } }

9

ES6

Object.assign(o1,o2) ; 
Object.assign({},o1,o2) ; //safe inheritance
var copy=Object.assign({},o1); // clone o1
//------Transform array of objects to one object---
var subjects_assess=[{maths:92},{phy:75},{sport:99}];
Object.assign(...subjects_assess); // {maths:92,phy:75,sport:99}

ES7 or Babel

{...o1,...o2} // inheritance
 var copy= {...o1};

1
@FilipBartuzi,你链接的不是ES6。但它基本上相当于lodash/underscore中的_.extend()函数。 - Nostalg.io

7

13
TC39已经不再使用.mixin - Knu
5
警告 - 这个回答已经不正确了,请查看Jack的答案获取正确和可行的方法。 - Benjamin Gruenbaum
Object.mixin已被Object.assign取代。 - gotofritz

5

不过要小心,至少在一个浏览器上(https://bugzilla.mozilla.org/show_bug.cgi?id=772334),这会影响性能。 - Reuben Morais
1
我认为最有趣的部分是defineProperties定义自己的属性。它不会覆盖[[prototype]]链上的属性,而是将其隐藏。 - RobG
2
不错的建议,虽然并不完全是扩展,因为它更多地用于定义属性应该如何行为... 直接执行 Object.defineProperties(obj1, obj2) 会导致意外的结果。 - balupton
如果属性是一个复杂值,你必须使用Object.getOwnPropertyDescriptor来设置它,否则你将会进行引用复制。 - danp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接