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'];
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'];
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]
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'
}
*/
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'
}
*/
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
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.", "☕️"]
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']
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']
🍵 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]
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)
Spreading plain single-dimension arrays into array is the great and undervalued way to clone the Array!
Hi Barbara Robles,
Your tips are very useful
Thanks for sharing
...rest is left
right is ...spread
This would definitely help me!
Thanks a lot
This post is of great help, thanks a lot
There's some mistake in the third code block 😕
You mean the changes made in the const a variable, right? Changed it! Thank you for pointing that out!
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?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!
It post made me learn alot! Thanks! 👑
....Nice explanation thank you = Thank you
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)