Pitfalls to avoid while using spread operator

Photo by Bogdan P. on Unsplash

Pitfalls to avoid while using spread operator

A scenario where I solved my TLE problem by removing spread operator in the code.

Recently I was solving a problem that required multiple array operations and combine the array into a single object so naturally like every time I manipulate the array, I used the spread operator to merge two large arrays and used Array.reduce() function to combine the array into single object, for each iteration in reduce I take the existing object apply spread operator on that to create a new object along with the new key value pair and return the new object the code looks something like below.

When I submit the answer I got TLE error. After lot of thinking then I finally found the solution.

  1. In line 5 to combine arrays I need to use the Array.concat() function.

  2. In the reduce function I always created a new object ({...acc}) everytime and returned it. Instead of that I have to mutate the same object(not recommended) with new key-value pairs.

final solution looks like below.

Let's discuss some learnings from this mistake. Let's discuss some learning I made while using the spread operator.

Working of Spread Operator

While the spread operator (...) offers a concise way to copy and merge objects and arrays in ES6, it's important to consider its performance implications. In older browsers that require polyfills, the spread operator might involve iterating through all key-value pairs and copying them one by one.

Potential Performance Issues:

  • Repeated Use inArray.reduce() or Lifecycle Hooks: Repeatedly using the spread operator within Array.reduce() or component lifecycle functions like ngOnChanges() or ngDoCheck() in Angular can lead to performance degradation. This is because each time the spread operator is used, it might trigger a full copy of the data.

  • Large Datasets: Working with very large datasets in the front-end and using the spread operator repeatedly can significantly impact performance. Copying a large number of key-value pairs can incur an O(n^2) time complexity, which becomes noticeable with extensive data.

Let's see some more points to remember while using spread operator.

Understanding Shallow Copies with the Spread Operator

The analysis of the spread operator is spot on! Here's a breakdown of the key points:

Spread Operator and Shallow Copies:

  • When you use the spread operator (...) to copy objects, it creates a shallow copy.

  • This means a new object is created with references to the original object's top-level properties.

  • If any of these properties are themselves objects, they are still referenced by the same object in memory.

Example Breakdown:

let person = {
  name: "Person Name",
  address: {
    street: "Street-no 1",
    area: "Area 51",
    city: "JT City",
  },
};

const newPerson = { ...person };

console.log(newPerson.address === person.address); // Output: true (same reference)
person.address["street"] = "street 2";
console.log(newPerson.address.street); // Output: street 2
  • In this example:

    • newPerson is a new object with a shallow copy of person.

    • Both newPerson.address and person.address point to the same object in memory (hence true for reference comparison).

    • Modifying person.address.steet directly affects the referenced object, reflected in newPerson.address.street as well.

Implications and Best Practices:

  • This behaviour can lead to unexpected side effects if you're not aware of shallow copies. Modifying one object can unintentionally change the other.

  • To avoid these surprises, consider these practices:

    • If you need a deep copy where nested objects are also copied independently, explore libraries like immer or lodash that provide deep copy functionalities.

    • Be mindful of when you use the spread operator and its implications for object references.

    • Consider alternative approaches if you need independent modifications of objects.

Spread operator read the values via "get" functions

While reading the properties and their value from source object, The spread operator uses the getter function to get the value of the property if it's defined on the property descriptor but after copying it into the target object the property on target object doesn't have a getter function.

Spread operator defines new properties

On the target function spread operator every time it defines new properties instead of assigning them to existing properties in the object. The inherited property descriptors are invalid up on the new object.

In other we can clone the objects using Object.assign(), using Object literals ({} syntax), JSON.parse(JSON.stringify()) other ways using some loadash function.

Conclusion

Should you be worried about the performance drawback on the spread operator? No, If you main Aim is to write good readable code and application is not data intensive you can use ( ...) syntax. It also promotes immutability in our application which is good for better testing and predictable functions. Modern browsers have good computation power that it in most of the times it won't effect the page response.

Sources

Feel free to correct me if I am wrong :)