DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

Cover image for Spread Operator: How Spread Works in JavaScript
Oluwatobi Sofela
Oluwatobi Sofela

Posted on • Updated on • Originally published at codesweetly.com

Spread Operator: How Spread Works in JavaScript

The spread operator (...) is a syntax that helps to expand iterables into individual elements.

The spread syntax serves within array literals, function calls, and initialized properties object to spread the values of iterable objects into separate items.

Note

A spread syntax is effective only when used within array literals, function calls, or initialized properties objects.

So, what exactly does this mean? Let’s see with some examples.

Spread Example 1: How spread works in an array literal

const myName = ["Sofela", "is", "my"];
const aboutMe = ["Oluwatobi", ...myName, "name."];

console.log(aboutMe);

// The invocation above will return:
[ "Oluwatobi", "Sofela", "is", "my", "name." ]
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

The snippet above used spread (...) to copy myName array into aboutMe.

Note

  • Alterations to myName will not reflect in aboutMe because all the values inside myName are primitives. Therefore, the spread operator simply copied and pasted myName’s content into aboutMe without creating any reference back to the original array.

  • As mentioned by @nombrekeff in the comment section, the spread operator only does shallow copy. So, keep in mind that supposing myName contained any non-primitive value, the computer would have created a reference between myName and aboutMe. See info 3 for more on how the spread operator works with primitive and non-primitive values.

  • Suppose we did not use the spread syntax to duplicate myName’s content. For instance, if we had written const aboutMe = ["Oluwatobi", myName, "name."]. In such a case, the computer would have assigned a reference back to myName. As such, any change made in the original array would reflect in the duplicated one.

Spread Example 2: How to use spread to convert a string into individual array items

const myName = "Oluwatobi Sofela";

console.log([...myName]);

// The invocation above will return:
[ "O", "l", "u", "w", "a", "t", "o", "b", "i", " ", "S", "o", "f", "e", "l", "a" ]
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

In the snippet above, we used the spread syntax (...) within an array literal object ([...]) to expand myName’s string value into individual items.

As such, "Oluwatobi Sofela" got expanded into [ "O", "l", "u", "w", "a", "t", "o", "b", "i", " ", "S", "o", "f", "e", "l", "a" ].

Spread Example 3: How the spread operator works in a function call

const numbers = [1, 3, 5, 7];

function addNumbers(a, b, c, d) {
  return a + b + c + d;
}

console.log(addNumbers(...numbers));

// The invocation above will return:
16
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

In the snippet above, we used the spread syntax to spread the numbers array’s content across addNumbers()’s parameters.

Suppose the numbers array had more than four items. In such a case, the computer will only use the first four items as addNumbers() argument and ignore the rest.

Here’s an example:

const numbers = [1, 3, 5, 7, 10, 200, 90, 59];

function addNumbers(a, b, c, d) {
  return a + b + c + d;
}

console.log(addNumbers(...numbers));

// The invocation above will return:
16
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

Here’s another example:

const myName = "Oluwatobi Sofela";

function spellName(a, b, c) {
  return a + b + c;
}

console.log(spellName(...myName));      // returns: "Olu"
console.log(spellName(...myName[3]));   // returns: "wundefinedundefined"
console.log(spellName([...myName]));    // returns: "O,l,u,w,a,t,o,b,i, ,S,o,f,e,l,aundefinedundefined"
console.log(spellName({...myName}));    // returns: "[object Object]undefinedundefined"
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

Spread Example 4: How spread works in an object literal

const myNames = ["Oluwatobi", "Sofela"];
const bio = { ...myNames, runs: "codesweetly.com" };

console.log(bio);

// The invocation above will return:
{ 0: "Oluwatobi", 1: "Sofela", runs: "codesweetly.com" }
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

In the snippet above, we used spread inside the bio object to expand myNames values into individual properties.

Important stuff to know about the spread operator

Keep these two essential pieces of info in mind whenever you choose to use the spread operator.

Info 1: Spread operators can’t expand object literal’s values

Since a properties object is not an iterable object, you cannot use the spread syntax to expand its values.

However, you can use the spread operator to clone properties from one object into another.

Here’s an example:

const myName = { firstName: "Oluwatobi", lastName: "Sofela" };
const bio = { ...myName, website: "codesweetly.com" };

console.log(bio);

// The invocation above will return:
{ firstName: "Oluwatobi", lastName: "Sofela", website: "codesweetly.com" };
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

The snippet above used the spread operator to clone myName’s content into the bio object.

Note

  • The spread operator can expand iterable objects’ values only.

  • An object is iterable only if it (or any object in its prototype chain) has a property with a @@iterator key.

  • Array, TypedArray, String, Map, and Set are all built-in iterable types because they have the @@iterator property by default.

  • A properties object is not an iterable data type because it does not have the @@iterator property by default.

  • You can make a properties object iterable by adding @@iterator onto it.

Info 2: The spread operator does not clone identical properties

Suppose you used the spread operator to clone properties from object A into object B. And suppose object B contains properties identical to those in object A. In such a case, B’s versions will override those inside A.

Here’s an example:

const myName = { firstName: "Tobi", lastName: "Sofela" };
const bio = { ...myName, firstName: "Oluwatobi", website: "codesweetly.com" };

console.log(bio);

// The invocation above will return:
{ firstName: "Oluwatobi", lastName: "Sofela", website: "codesweetly.com" };
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

Observe that the spread operator did not copy myName’s firstName property into the bio object because bio already contains a firstName property.

Info 3: Beware of how spread works when used on objects containing non-primitives!

Suppose you used the spread operator on an object (or array) containing only primitive values. The computer will not create any reference between the original object and the duplicated one.

For instance, consider this code below:

const myName = ["Sofela", "is", "my"];
const aboutMe = ["Oluwatobi", ...myName, "name."];

console.log(aboutMe);

// The invocation above will return:
["Oluwatobi", "Sofela", "is", "my", "name."]
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

Observe that every item in myName is a primitive value. Therefore, when we used the spread operator to clone myName into aboutMe, the computer did not create any reference between the two arrays.

As such, any alteration you make to myName will not reflect in aboutMe, and vice versa.

Take for an example, let’s add more content to myName:

myName.push("real");
Enter fullscreen mode Exit fullscreen mode

Now, let’s check the current state of myName and aboutMe:

console.log(myName); // ["Sofela", "is", "my", "real"]
console.log(aboutMe); // ["Oluwatobi", "Sofela", "is", "my", "name."]
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

Notice that myName’s updated content did not reflect in aboutMe β€” because spread created no reference between the original array and the duplicated one.

What if myName contains non-primitive items?

Suppose myName contained non-primitives. In that case, spread will create a reference between the original non-primitive and the cloned one.

Here is an example:

const myName = [["Sofela", "is", "my"]];
const aboutMe = ["Oluwatobi", ...myName, "name."];

console.log(aboutMe);

// The invocation above will return:
[ "Oluwatobi", ["Sofela", "is", "my"], "name." ]
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

Observe that myName contains a non-primitive value.

Therefore, using the spread operator to clone myName’s content into aboutMe caused the computer to create a reference between the two arrays.

As such, any alteration you make to myName’s copy will reflect in aboutMe’s version, and vice versa.

Take for an example, let’s add more content to myName:

myName[0].push("real");
Enter fullscreen mode Exit fullscreen mode

Now, let’s check the current state of myName and aboutMe:

console.log(myName); // [["Sofela", "is", "my", "real"]]
console.log(aboutMe); // ["Oluwatobi", ["Sofela", "is", "my", "real"], "name."]
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

Notice that myName’s updated content is reflected in aboutMe β€” because spread created a reference between the original array and the duplicated one.

Here’s another example:

const myName = { firstName: "Oluwatobi", lastName: "Sofela" };
const bio = { ...myName };

myName.firstName = "Tobi";

console.log(myName); // { firstName: "Tobi", lastName: "Sofela" }
console.log(bio); // { firstName: "Oluwatobi", lastName: "Sofela" }
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

In the snippet above, myName’s update did not reflect in bio because we used the spread operator on an object that contains primitive values only.

Note

A developer would call myName a shallow object because it contains only primitive items.

Here’s one more example:

const myName = { 
  fullName: { firstName: "Oluwatobi", lastName: "Sofela" }
};

const bio = { ...myName };

myName.fullName.firstName = "Tobi";

console.log(myName); // { fullName: { firstName: "Tobi", lastName: "Sofela" } }
console.log(bio); // { fullName: { firstName: "Tobi", lastName: "Sofela" } }
Enter fullscreen mode Exit fullscreen mode

Try it on StackBlitz

In the snippet above, myName’s update is reflected in bio because we used the spread operator on an object that contains a non-primitive value.

Note

  • A developer would call myName a deep object because it contains a non-primitive item.

  • You do shallow copy when you create references while cloning one object into another. For instance, ...myName produces a shallow copy of the myName object because whatever alteration you make in one will reflect in the other.

  • You do deep copy when you clone objects without creating references. For instance, I could deep copy myName into bio by doing const bio = JSON.parse(JSON.stringify(myName)). By so doing, the computer will clone myName into bio without creating any reference.

  • You can break off the reference between the two objects by replacing the fullName object inside myName or bio with a new object. For instance, doing myName.fullName = { firstName: "Tobi", lastName: "Sofela" } would disconnect the pointer between myName and bio.

Wrapping it up

This article discussed what the spread operator is. We also looked at how spread works in array literals, function calls, and object literals.

Now that we know how spread works, let’s discuss the rest operator in this article so we can see the differences.

Thanks for reading!

Top comments (3)

Collapse
nombrekeff profile image
Keff

Nice article! Useful examples

I wanted to add something else, as I don't see many people aknowledging it. You say:

Alterations to myName will not reflect in aboutMe because the spread operator simply copied and paste myName’s content into aboutMe without creating any reference back to the original array.

This is true as long as the items inside the arrays are non-objects. As it only does shallow copy.

If you have this example:

const myName = [{ name: 'Giovanni' }];
const aboutMe = [{ description: 'I can do backflip' }];

const me = [...myName, ...aboutMe];
Enter fullscreen mode Exit fullscreen mode

In this case, if you modify an object in the me array, it will reflect in the original arrays, as they are just references.

I like to advice on this, as I've seen it cause some big issues.

Collapse
oluwatobiss profile image
Oluwatobi Sofela Author

Thanks, Keff, for highlighting this significant omission. I have now updated the article to reflect how spread works on objects with non-primitive values.

Collapse
nombrekeff profile image
Keff

No problem, I'm becoming the "spread-shallow-copy" guy lol!

🌚 Life is too short to browse without dark mode