Object spread vs. Object.assign
For reference object rest/spread is finalised in ECMAScript 2018 as a stage 4. The proposal can be found here.
For the most part object assign and spread work the same way, the key difference is that spread defines properties, whilst Object.assign() sets them. This means Object.assign() triggers setters.
It's worth remembering that other than this, object rest/spread 1:1 maps to Object.assign() and acts differently to array (iterable) spread. For example, when spreading an array null values are spread. However using object spread null values are silently spread to nothing.
Array (Iterable) Spread Example
const x = [1, 2, null , 3];
const y = [...x, 4, 5];
const z = null;
console.log(y); // [1, 2, null, 3, 4, 5];
console.log([...z]); // TypeError
Object Spread Example
const x = null;
const y = {a: 1, b: 2};
const z = {...x, ...y};
console.log(z); //{a: 1, b: 2}
This is consistent with how Object.assign() would work, both silently exclude the null value with no error.
const x = null;
const y = {a: 1, b: 2};
const z = Object.assign({}, x, y);
console.log(z); //{a: 1, b: 2}
This isn't necessarily exhaustive.
Spread syntax
options = {...optionsDefault, ...options};
Advantages:
If authoring code for execution in environments without native support, you may be able to just compile this syntax (as opposed to using a polyfill). (With Babel, for example.)
Less verbose.
Disadvantages:
When this answer was originally written, this was a proposal, not standardized. When using proposals consider what you'd do if you write code with it now and it doesn't get standardized or changes as it moves toward standardization. This has since been standardized in ES2018.
Literal, not dynamic.
Object.assign()
options = Object.assign({}, optionsDefault, options);
Advantages:
Standardized.
Dynamic. Example:
var sources = [{a: "A"}, {b: "B"}, {c: "C"}]; options = Object.assign.apply(Object, [{}].concat(sources)); // or options = Object.assign({}, ...sources);
Disadvantages:
- More verbose.
- If authoring code for execution in environments without native support you need to polyfill.
This is the commit that made me wonder.
That's not directly related to what you're asking. That code wasn't using Object.assign()
, it was using user code (object-assign
) that does the same thing. They appear to be compiling that code with Babel (and bundling it with Webpack), which is what I was talking about: the syntax you can just compile. They apparently preferred that to having to include object-assign
as a dependency that would go into their build.
I think one big difference between the spread operator and Object.assign
that doesn't seem to be mentioned in the current answers is that the spread operator will not copy the the source object’s prototype to the target object. If you want to add properties to an object and you don't want to change what instance it is of, then you will have to use Object.assign
.
Edit: I've actually realised that my example is misleading. The spread operator desugars to Object.assign
with the first parameter set to an empty object. In my code example below, I put error as the first parameter of the Object.assign
call so the two are not equivalent. The first parameter of Object.assign
is actually modified and then returned which is why it retains its prototype. I have added another example below:
const error = new Error();
error instanceof Error // true
const errorExtendedUsingSpread = {
...error,
...{
someValue: true
}
};
errorExtendedUsingSpread instanceof Error; // false
// What the spread operator desugars into
const errorExtendedUsingImmutableObjectAssign = Object.assign({}, error, {
someValue: true
});
errorExtendedUsingImmutableObjectAssign instanceof Error; // false
// The error object is modified and returned here so it keeps its prototypes
const errorExtendedUsingAssign = Object.assign(error, {
someValue: true
});
errorExtendedUsingAssign instanceof Error; // true
See also: https://github.com/tc39/proposal-object-rest-spread/blob/master/Spread.md