2.1 Object spread rule: latter property wins

When multiple objects are spread and some properties have same keys, how do the final the final set of values is calculated? The rule is simple: latter spread property overwrites earlier properties that have the same key.

Let's continue with a few examples. The following object literal instantiates a cat:

const cat = {
sound: 'meow',
legs: 4
};

Let's play Dr. Frankenstein and transform this cat into a dog. Pay attention to the value of .sound property:

The cat remains a cat. Although the first source object provides .sound property with value 'woof', it's overwritten by latter 'meow' value from cat spread properties.

The relative position of object spreads and regular properties is important. Such effect of spread syntax permits the implementation of recipes like object cloning, merging objects, filling with defaults.

Let's details into these recipes.

2.2 Cloning an object

Coning an object using spread syntax is short and expressive. The following example creates a clone of bird object:

__proto__ inside the object literal ensures that doomFullClone has the necessary prototype Game.prototype.

Don't try this at home: __proto__ is deprecated. I'm using it just for demonstration.

Object spread lags on instances created from constructor invocation since it doesn't keep the prototype. The intention is to spread own and enumerable properties in a shallow manner, so the approach to ignore the prototype seems reasonable.

As a side note, there's a more reasonable way to clone doom using Object.assign():

2.3 Immutable object update

When the same object is shared across many places of an application, a direct modification of it might lead to unexpected side effects. Tracing such modifications is a tedious task.

A better approach is to make operations immutable. Immutability keeps under better control object's modification and favors writing pure functions. Even in complex scenarios it's easier to determine the source and reason of an object update, since data flows into a single direction.

Object spread is convenient to modify objects in an immutable manner. Say you have an object that describes an edition of a book:

The first object spread ...part1 sets the value of .configuration to 'sedan'. Nevertheless the latter object spread ...part3 overwrites the previous .configuration value, making it finally 'hatchback'.

2.5 Filling an object with defaults

An object can have different sets of properties on runtime. Some properties might be set, others might be missing.

Such scenario can happen in case of a configuration object. User can specify only significant properties of the configuration, but unspecified properties are taken from defaults.

Let's implement a multiline(str, config) function that breaks str into multiple lines at a given width.

As result safeConfig has the full set of properties that multiline() main code can use. No matter the input config, which can miss some properties, you are confident that safeConfig has the necessary values.

Object spread's implementation of defaults is intuitive, which is great.

2.6 "We need to go deeper"

The cool thing about object spread is the possibility to use on nested objects. That's a great readability win when updating a big object, and is recommended over Object.assign() alternative.

The destructuring assignment defines a new variable width and sets its value to style.width. Object rest ...margin within destructuring assignment collects the rest of the properties marginLeft and marginRight into the object margin.

Object rest collects only own and enumerable properties.

Notice that object rest must be the last element in the destructuring assignment.
Hence the code const { ...margin , width } = style is invalid and would trigger a SyntaxError: Rest element must be last element.

4. Conclusion

Object spread has a few rules to remember:

It extracts own and enumerable properties from the source object

Latter spread property overwrites earlier ones with the same key

At the same time object spread is short and expressive, works nicely on nested objects, while keeping the immutability of updates. It enables easy implementation of objects cloning, merging and filling with default properties.

Collecting the rest of properties after a destructuring assignment is achieved by object rest syntax.

Indeed object rest and spread properties are great additions to JavaScript.

What cool object rest/spread recipes do you know? Write a comment below!