Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at the best features of ES2018.
Cloning Object Prototype with Properties
We can clone the object’s prototype with properties.
To do that, we can set the __proto__
property to the original object’s prototype:
const obj = {
a: 1,
b: 2
};
const clone = {
__proto__: Object.getPrototypeOf(obj),
...obj
};
We set the prototype and we spread the other properties to get a more comprehensive clone.
This is the same as:
const clone = Object.assign(
Object.create(Object.getPrototypeOf(obj)), obj);
Merging Two Objects
The object spread operator is also handy for merging multiple objects into one.
For instance, we can write:
const merged = {
...obj1,
...obj2
};
which is the same as:
const merged = Object.assign({}, obj1, obj2);
Merging objects is useful for doing things like merging options with the default options:
const data = {
...DEFAULTS,
...options
};
Spreading Objects versus Object.assign()
The spread operator and Object.assign
are very similar.
The difference is that spread define new properties, while Object.assign
set them.
For instance, if we have:
Object.assign(target, obj1, obj2);
then target
is modified in place with the properties of obj1
and obj2
.
If we want to create a new merged object with the properties from all 3 objects, we can write:
const merged = Object.assign({}, target, obj1, obj2);
It returns a new object with the properties from target
, obj1
, and obj2
.
The spread operator is the same as the 2nd way of using Object.assign
.
Spread and Object.assign
both read values with a get
operation.
For instance, if we have:
const obj = {
get a() {
return 1
}
}
const clone = Object.assign({}, obj);
Then the a
getter is cloned in clone
.
If we have:
const obj = {
get a() {
return 1
}
}
const clone = {
...obj
};
Then we get the same result.
The spread operator defines new properties in the target object.
But Object.assign
uses a normal set operation to create them.
If we add a setter to the Object.prototype
, create the object, and run Object.assign
on it:
Object.defineProperty(Object.prototype, 'foo', {
set(value) {
console.log('set', value);
},
});
const obj = {
foo: 1
};
const clone = `Object.assign({}, obj);`
then the setter is run as we can see from the console log.
On the other hand, if we have:
Object.defineProperty(Object.prototype, 'foo', {
set(value) {
console.log('set', value);
},
});
const obj = {
foo: 1
};
const clone = {
...obj
}
then the setter isn’t run.
With Object.assign
, we can stop it from creating non-inherited properties via inherited read-only properties.
For instance, we can write:
Object.defineProperty(Object.prototype, 'foo', {
writable: false,
value: 1,
});
const obj = {
foo: 1
};
const clone = Object.assign({}, obj);
Then we get ‘Uncaught TypeError: Cannot assign to read-only property ‘foo’ of object ‘#<Object>’’.
On the other hand, if we have:
Object.defineProperty(Object.prototype, 'foo', {
writable: false,
value: 1,
});
const obj = {
foo: 1
};
const clone = {
...obj
};
Then we don’t get any errors, so the property is rated successfully.
This is because the spread operator defines properties rather than setting them.
Conclusion
Object.assign
does similar things, but there are small differences that we can’t overlook.