DEV Community

Cover image for Spread vs Rest Operators in JavaScript

Spread vs Rest Operators in JavaScript

Content:

Let's learn how to use the three dots in our code. After this article, you won't forget the difference between them! 🐱🐱🐱

Definition :

  • The spread is the operator that allows us to expand iterables into individual elements.

  • The rest is the operator we use to represent an indefinite number of arguments in an array.

Both are written using the three dots ..., but you'll see that it's easy to identify when it's a spread and when it's a rest! At the end, there's also a good tip for you!

Spread Operator

Concatenating Arrays

The spread allows us to combine two or more arrays, maintaining a concise and clean language.

Let's have some coffee? ☕️ Consider the two arrays below:

const coffee = ['coffee', 'water'];
const spices = ['cinnamon', 'nutmeg', 'cardamom'];
Enter fullscreen mode Exit fullscreen mode

We can use the spread to mix the ingredients into a single array:

const coffeeReady = [...coffee, ...spices];

console.log(coffeeReady) 
// output:
// ['coffee', 'water', 'cinnamon', 'nutmeg', 'cardamom'];
Enter fullscreen mode Exit fullscreen mode

Simple! Much better than writing item by item from each of the two arrays to form coffeeReady.

Important:

1. Changes in coffee will NOT affect coffeeReady!

To better understand: when we make a copy, we can either create a new reference to the original value or just copy the value. Creating a new reference is simply creating a variable that will point to the same location in memory where the original value is.

If we had created a new reference, any changes in coffee would change coffeeReady and vice versa. But what we did with the spread was to copy only the value, which in turn will be stored in another location in memory. Thus, changes in one array will not affect the other.

However, some details could change this picture! That's because...

2. The spread only creates a shallow copy!

This means that, depending on the data contained in coffee, some changes could indeed alter coffeeReady! If coffee contained some non-primitive value, the computer would have created a reference to the values in memory. So, any change in one array would affect the other, since both would be storing a reference to the same location in memory. See below:

let a = [1, [2, 3]];
// [2, 3] is an array nested in a, and therefore
// it is a non-primitive value
const b = [4, 5, 6];

let c = [...a, ...b];
console.log(c);
// output: [1, [2, 3], 4, 5, 6]

a[0] = 11;
a[1][0] = 22;

console.log(c);
// output: [1, [22, 3], 4, 5, 6]
Enter fullscreen mode Exit fullscreen mode

See above that changing a[0] did not affect c, because we changed a primitive value. In other words, a[0] and c point to equal values, but they are in different locations in memory. However, changing a[1][0] modified c, because we changed the value to which both a[1][0] and c point.

Merging Objects

We can also merge objects into one using the spread:

const myParents = { 
  fathersName: 'Michael', 
  mothersName: 'Louise'
};
const mySiblings = { 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
};

const myFamily = { ...myParents, ...mySiblings };
console.log(myFamily);
/* output:
{ 
  fathersName: 'Michael', 
  mothersName: 'Louise', 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
} 
*/
Enter fullscreen mode Exit fullscreen mode

However, it is important to remember that the spread does not clone identical properties! Below we have two objects with brothersName:

const myParents = { 
  fathersName: 'Michael', 
  mothersName: 'Louise',
  brothersName: 'Gabriel'
};
const mySiblings = { 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
};

const myFamily = { ...myParents, ...mySiblings };
console.log(myFamily);
/* output:
{ 
  fathersName: 'Michael', 
  mothersName: 'Louise', 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
}
*/
Enter fullscreen mode Exit fullscreen mode

Note that the final object does not inherit both brothersName keys. In fact, only one prevails, which is from the second object.

Copying Arrays and Objects

Got the idea so far? If we can merge arrays and also objects, that means we can also copy them individually:

// creating a shallow copy of coffee:
const coffee = ['coffee', 'water'];
const coffeeCopy = [...coffee];
console.log(coffeeCopy)
// output: 
// ['coffee', 'water'];

// creating a shallow copy of mySiblings:
const mySiblings = { 
  brothersName: 'Philip', 
  sistersName: 'Lara' 
};

const myFamily = { 
  fathersName: 'Michael', 
  mothersName: 'Louise',
  ...mySiblings
};
// Now we can treat brothersName and sistersName as 
// a property of myFamily:
console.log(myFamily.brothersName) 
// output: Philip
Enter fullscreen mode Exit fullscreen mode

Turning Strings into Arrays

It is also possible to use the spread to turn a string into an array. This allows us to have more flexibility when manipulating strings, since we will be able to apply array methods to strings:

const str = 'coffee';
const letters = [...str, 's.', '☕️']; 
console.log(letters);// ["c", "o", "f", "f", "e", "e", "s.", "☕️"]
Enter fullscreen mode Exit fullscreen mode

Rest Operator

As mentioned above, the rest operator is used to pack elements into an array. You'll see that the rest operator is a great ally when dealing with many values or an uncertain number of values.

Function Parameters

The rest operator allows us to represent an indefinite number of arguments as an array.

const order = function(beverage, ...otherIngredients) {
  console.log(beverage);
  console.log(otherIngredients);
};

order('green tea', 'milk', 'brown sugar'); 
// output:
// green tea 
// ['milk', 'brown sugar']
Enter fullscreen mode Exit fullscreen mode

Note that this allows us to call the same function with more arguments, as the rest operator will put them all in the otherIngredients array:

const order = function(beverage, ...otherIngredients) {
  console.log(beverage);
  console.log(otherIngredients);
};

order('green tea', 'milk', 'brown sugar', 'mint', 'tonka'); 
// output:
// green tea 
// ['milk', 'brown sugar', 'mint', 'tonka']
Enter fullscreen mode Exit fullscreen mode

🍵 An important detail is that the rest must be the last parameter in the function! If we wrote function(...otherIngredients, beverage) the computer wouldn't know when to stop and this code would generate an error.

Destructuring with Rest and Spread

Rest and spread are also widely used in destructuring. If you don't know what that is yet, I suggest my other two articles here: array destructuring and object destructuring.

Using rest:

const [a, b, ...others] = [1, 2, 3, 4, 5];
console.log(a, b, others); 
// output: 1 2 [3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Now, take this tip so you don't confuse rest with spread here: the rest is on the left side of = and the spread is on the right side of =.

...rest = ☕️

🍵 = ...spread

Thanks for making it this far! I hope this post has helped you! Thoughts? Please, comment below!

Top comments (11)

Collapse
 
smart_egg profile image
Dmitriy A.

Spreading plain single-dimension arrays into array is the great and undervalued way to clone the Array!

Collapse
 
jangelodev profile image
João Angelo

Hi Barbara Robles,
Your tips are very useful
Thanks for sharing

Collapse
 
faisalkhan4k profile image
Mohammed Faisal Khan

...rest is left
right is ...spread

This would definitely help me!
Thanks a lot

Collapse
 
vaddijaswant profile image
Jaswant Vaddi

This post is of great help, thanks a lot

Collapse
 
sh20raj profile image
Sh Raj

There's some mistake in the third code block 😕

Collapse
 
baroblesvi profile image
Barbara Robles (english prof.)

You mean the changes made in the const a variable, right? Changed it! Thank you for pointing that out!

Collapse
 
stevem profile image
steve

It was a good detailed article. One query, the rest is on the left side of = by left side do you mean when we declare functions or destructure variables?

Collapse
 
baroblesvi profile image
Barbara Robles (english prof.)

Thank you for your comment! Actually, I meant for destructuring! When it comes to functions, the rest is used on function definitions, while the spread operator is used when calling the function! Hope this helps!

Collapse
 
hasibrashid profile image
Hasib Al Rashid

It post made me learn alot! Thanks! 👑

Collapse
 
devsk001 profile image
Dev Sk

....Nice explanation thank you = Thank you

Collapse
 
starswan profile image
Stephen Dicks

Does anyone else struggle with this syntax? A 'concise and clean language' seems a strange phrase for JavaScript - I find this extremely hard to read. I also think that functions with varadic arguments (the rest syntax) should mostly be avoided in application code (which is what the vast majority of us are writing)