Spread vs Rest Operators in JavaScript
Tags: javascript, beginners, webdev, frontend
Published: 2024-03-12 12:42:40

Content:

  * [Definition] (#definition)
  * [Spread Operator] (#spread-op)
    * [Concatenating Arrays] (#concatenating)
    * [Merging Objects] (#merging-objects)
    * [Copying Arrays and Objects] (#copying)
    * [Turning Strings into Arrays] (#strings-into-arrays)
  * [Rest Operator] (#rest-op)
    * [Function Parameters] (#function-parameters)
  * [Destructuring with Rest and Spread] (#destructuring)

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

###Definition <a name="definition"></a>:

* 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 <a name="spread-op"></a>

####Concatenating Arrays <a name="concatenating"></a>

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:

```javascript
const coffee = ['coffee', 'water'];
const spices = ['cinnamon', 'nutmeg', 'cardamom'];
```
We can use the spread to mix the ingredients into a single array:

```javascript
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:

```javascript
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 <a name="merging-objects"></a>

We can also merge objects into one using the spread:

```javascript
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`:

```javascript
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 <a name="copying"></a>

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

```javascript
// 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 <a name="strings-into-arrays"></a>

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:

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

###Rest Operator <a name="rest-op"></a>

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 <a name="function-parameters"></a>

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

```javascript
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:

```javascript
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 <a name="destructuring"></a>

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](https://dev.to/baroblesvi/destructuring-arrays-in-javascript-4c5n) and [object destructuring](https://dev.to/baroblesvi/object-destructuring-in-javascript-39g6).

Using rest:
```javascript
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!