How to Merge Objects in JavaScript

Mark O'Neill
Share

Developers often need to merge or copy objects for tasks like combining data or creating new instances. Techniques like the spread ( ... ) operator (used for merging properties of multiple objects) and the Object.assign() method (for copying properties from one object to another) are essential tools for these tasks. However, understanding when and how to use them is crucial for effective object manipulation. In this article, I’ll cover some practical applications of these methods, their strengths and weaknesses, and the concept of deep copying before merging nested objects.

Approaches for Merging Objects

1. The Spread Operator ( ... )

The spread operator (...) is a common approach to merge objects in JavaScript. It has the form {...object1, ...object2}. When properties with the same keys exist in the source objects, the spread operator overwrites the values in the target object with the latest source object’s values.

const defaults = { color: 'red', size: 'medium' };
const userSettings = { color: 'blue' };

const combinedSettings = { ...defaults, ...userSettings };
console.log(combinedSettings);
// Output: { color: 'blue', size: 'medium' }

2. The Object.assign() Method

Object.assign() is a JavaScript method for merging objects. Its syntax is Object.assign(target, source1, source2, ...), where you merge source objects into the target object. When properties with the same keys exist in the source objects, Object.assign() overwrites the values in the target object with the latest source object’s values.

const defaults = { color: 'red', size: 'medium' };
const userSettings = { color: 'blue' };

const combinedSettings = Object.assign({}, defaults, userSettings);
console.log(combinedSettings);
// Output: { color: 'blue', size: 'medium' }

Pitfalls and Considerations

Here are the potential pitfalls and issues with both the spread operator and Object.assign() method for merging objects in JavaScript:

1. Shallow copying

Both the spread operator and Object.assign() perform shallow copying when merging objects. This means that nested objects are still references to the original objects. Modifying the nested objects in the merged object can affect the original objects, which may lead to unintended side effects.

See Deep Merging below.

2. Overwriting properties

When merging objects with properties that have the same keys, both the spread operator and Object.assign() overwrite the values in the resulting object with values from the latest source object. This behavior may cause the loss of data if not handled carefully.

3. Compatibility issues

The spread operator is part of ECMAScript 2015 (ES6) and is not supported in older JavaScript environments or browsers, such as Internet Explorer. This may cause compatibility issues if your code needs to run in older environments. In such cases, it’s better to use Object.assign(), which has wider support.

4. Non-enumerable properties

Both the spread operator and Object.assign() only copy enumerable properties from source objects to the target object. Non-enumerable properties are not copied during the merging process, which may lead to missing data or unexpected behavior.

5. Performance concerns

In cases where you need to merge large objects or perform merging operations frequently, using Object.assign() or the spread operator may cause performance issues due to the creation of new objects during the merging process.

6. Prototype properties

Object.assign() copies properties from the source object’s prototype to the target object, which may lead to unexpected behavior if the source object’s prototype has properties that conflict with the target object’s properties. The spread operator, on the other hand, does not copy prototype properties.

It’s essential to be aware of these pitfalls and issues when using the spread operator or Object.assign() to merge objects in JavaScript. In specific scenarios, you may need to employ alternative approaches, such as deep cloning or deep merging functions, to overcome these limitations.

Which One to Use

Both Object.assign() and the spread operator effectively merge objects. The spread operator is more concise and modern, while Object.assign() offers better compatibility with older JavaScript environments.

To decide which method to use, consider:

  1. If your environment supports the spread operator (such as the latest ECMAScript version), use it for its concise syntax.
  2. If compatibility with older JavaScript environments is essential, choose Object.assign().
  3. If you need to copy a nested object (one with objects nested inside it) read on to Deep Copying Objects.

Deep Merging: Deep Copy & Merge Objects

Both the spread operator and Object.assign() create a shallow copy of the object(s) being copied. Essentially this means the new object will have references to the same nested objects (e.g. arrays and functions) as the original object, rather than having copies of them.

Knowing and avoiding this is essential before merging objects together.

The example below shows how editing nested objects on a copied object can affect the original:

const planet = {
  name: 'Earth',
  emoji: '🌍',
  info: {
    type: 'terrestrial',
    moons: 1
  }
};

// Shallow copy using the spread operator
const shallowCopyPlanet = { ...planet };

// Modifying the nested object in the shallow copy
shallowCopyPlanet.info.moons = 2;

console.log('Original planet:', planet.info.moons);
// Original planet: 2
console.log('Shallow copy of the planet:', shallowCopyPlanet.info.moons);
// Shallow copy of the planet: 2

The output of this code shows that the info.moons property on the original object planet has been changed by editing it in shallowCopyPlanet (You probably don’t want this).

A custom Deep Merging function

Here’s a function that deep copies multiple objects before merging them and returns a single object. In the code, the deepMergeObjects function takes any number of input objects, creates deep copies of them using the JSON.parse(JSON.stringify()) technique, and then merges them using the spread operator inside the reduce() method. The merged object will contain deep copies of the properties from the input objects.

const deepMergeObjects = (...objects) => {
  const deepCopyObjects = objects.map(object => JSON.parse(JSON.stringify(object)));
  return deepCopyObjects.reduce((merged, current) => ({ ...merged, ...current }), {});
}

// Example usage:
const countries = {
  USA: {
    capital: 'Washington D.C.',
    emoji: '🇺🇸',
    population: 331000000
  }
};

const countriesDetails = {
  USA: {
    language: 'English',
    currency: 'USD'
  },
  Germany: {
    capital: 'Berlin',
    emoji: '🇩🇪',
    language: 'German',
    currency: 'EUR',
    population: 83000000
  }
};

const mergedData = deepMergeObjects(countries, countriesDetails);

Conclusion

Thanks for reading! I hope this article has given you a deep understanding of merging objects in JavaScript and not just a shallow introduction. Being able to combine objects in this should merge nicely with your JavaScript skills and spread your coding prowess. For any questions or comments, join us over at the SitePoint Community Forum.