163

I'm sick & tired of always having to write code like this:

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

Or if I don't want to write the code myself, implement a library that does it already. Surely ES6+ is coming to the rescue on this will provide us with something like a Object.prototype.extend(obj2...) or Object.extend(obj1,obj2...)

So does ES6+ provide such functionality? If not already there, then is such functionality planned? If not planned, then why not?

Junaid
  • 4,682
  • 1
  • 34
  • 40
balupton
  • 47,113
  • 32
  • 131
  • 182
  • 3
    So why haven't you added it to **your** library? – RobG Dec 13 '12 at 05:10
  • 13
    @RobG this question is about the hope that ES6 will remove us from having to need such boilerplate crap in the first place.For what it's worth: https://github.com/balupton/bal-util/blob/aaade3e4f34e7590ba31fa55a0b6e1df044d10b8/src/lib/flow.coffee#L68-L91 – balupton Dec 13 '12 at 19:59
  • I don't think there is a general way to copy the name/value pairs from one object to another. Do you only deal with own properties or those on the `[[Prototype]]` chain? Do you do "deep" or "shallow" copies? What about non–enumerable and non–writable properties? I think I'd rather have a small library function that does what I need, and mostly it's avoidable anyway. – RobG Dec 13 '12 at 23:23
  • See also [How can I merge properties of two JavaScript objects dynamically?](https://stackoverflow.com/q/171251/1048572) for non-ES6 solutions – Bergi Nov 28 '21 at 01:17

6 Answers6

220

You will be able to do a shallow merge/extend/assign in ES6 by using Object.assign:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Syntax:

Object.assign(target, sources);

where ...sources represents the source object(s).

Example:

var obj1 = {name: 'Daisy', age: 30};
var obj2 = {name: 'Casey'};

Object.assign(obj1, obj2);

console.log(obj1.name === 'Casey' && obj1.age === 30);
// true
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
Jack
  • 9,448
  • 3
  • 29
  • 33
  • 75
    A small note: Object.assign mutates the target. If that's not what you want you can call it with an empty object: `let merged = Object.assign({}, source1, source2);` – david Feb 23 '16 at 00:04
  • 21
    Please notice this is a shallow extend... nested objects are NOT merged – monzonj Mar 23 '16 at 10:07
  • @monzonj, isn't there any option to extend nested objects, without using lodash or mout? – Joao Falcao Nov 10 '16 at 17:36
  • 1
    There is the good way to merge deeply nested objects without a library using just `Object.assign`: [see my answer here](https://stackoverflow.com/questions/27936772/how-to-deep-merge-instead-of-shallow-merge/44223050#44223050) – Ruslan May 28 '17 at 01:46
  • An old way to extend nested objects is using `JSON.parse(JSON.stringify(src))` – Andre Figueiredo Jan 08 '18 at 17:53
183

You can use the object spread syntax for this:

const merged = {...obj1, ...obj2}

For arrays the spread operator was already part of ES6 (ES2015), but for objects it was added to the language spec at ES9 (ES2018). Its proposal as been enabled by default in tools like Babel long before that.

Thijs Koerselman
  • 21,680
  • 22
  • 74
  • 108
  • 3
    No officially it is called ES2015 now :P Since when is destructuring not part of ES6? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment Running babel-node: ```const ob1 = {foo: 123}; const ob2 = {bar: 234}; const merged = {...ob1, ...ob2}; console.log(merged)``` Output: ```{ foo: 123, bar: 234 }``` – Thijs Koerselman Sep 04 '15 at 18:52
  • 9
    That's not destructuring, it's spreading - and no, for objects it's not part of ES6. You should disable experimental ES7 drafts in your babel. – Bergi Sep 04 '15 at 21:54
  • 2
    Ah forgive me. You are right. It is a babel stage 2 feature at the moment. https://github.com/sebmarkbage/ecmascript-rest-spread I never realized that because I've been using it from the start with babel and it's enabled by default. But since you need to transpile anyway, and the object spread is a pretty straightforward thing I would recommend it anyway. I love it. – Thijs Koerselman Sep 04 '15 at 22:07
  • They're enabled by default, really? That sounds like a bug. – Bergi Sep 04 '15 at 22:17
  • 2
    "Proposals that are stage 2 or above are enabled by default". https://babeljs.io/docs/usage/experimental/ – Thijs Koerselman Sep 05 '15 at 07:37
  • This JS feature could be enabled individually in babel with "plugins: [ 'transform-object-rest-spread'] " – Daniil Iaitskov Feb 20 '18 at 10:44
  • 4
    NOTE: When the same property exists in both `obj1` and `obj2`, the value from the right-most (latter) object takes precedence (`obj2`) – rinogo Nov 18 '20 at 16:57
17

I know this is a bit of an old issue but the easiest solution in ES2015/ES6 is actually quite simple, using Object.assign(),

Hopefully this helps, this does DEEP merging as well:

/**
 * 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;
}

Example usage:

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] } } }
Salakar
  • 6,016
  • 2
  • 18
  • 24
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};
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
  • 1
    @FilipBartuzi that's not ES6 that you linked to. But it is basically doing the equivalent to _.extend() in lodash/underscore. – Nostalg.io Aug 22 '17 at 18:26
7

The addition of Object.mixin is currently being discussed to take care of the behavior you are asking for. https://mail.mozilla.org/pipermail/es-discuss/2012-December/027037.html

Although it is not in the ES6 draft yet, it seems like there is a lot of support for it, so I think it will show up in the drafts soon.

Nathan Wall
  • 10,530
  • 4
  • 24
  • 47
5

Perhaps the ES5 Object.defineProperties method will do the job?

e.g.

var a = {name:'fred'};
var b = {age: {value: 37, writeable: true}};

Object.defineProperties(a, b);

alert(a.age); // 37

MDN documentation: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperties

RobG
  • 142,382
  • 31
  • 172
  • 209
  • Be careful, though. On [at least one browser](https://bugzilla.mozilla.org/show_bug.cgi?id=772334) this has performance implications. – Reuben Morais Dec 13 '12 at 05:24
  • 1
    I think the most interesting part is that `defineProperties` defines own properties. It doesn't overwrite properties on the `[[prototype]]` chain, it shadows them. – RobG Dec 13 '12 at 11:31
  • 2
    Good suggestion, though not really an extend as it is more for defining how properties should behave... Doing a straightforward Object.defineProperties(obj1,obj2) would cause unexpected results. – balupton Dec 13 '12 at 20:00
  • would have to use `Object.getOwnPropertyDescriptor` also to set the property when it is a complex value, or you will copy by reference. – danp Sep 26 '13 at 15:05