Rest and Spread
Tags: javascript, webdev, beginners, tutorial
Published: 2021-10-05 17:10:22

*This article is part of the first installment of a series I'm calling __JavaScript You Must Know__. The aim of this series is to go over various fundamentals of the JavaScript language that a developer must know/understand before moving forward with any of the modern JavaScript frameworks like React, Angular, and Vue. Learning these fundamentals in parallel with the frameworks would also be acceptable.*

*This first installment of the series focuses on syntax-related topics and is called __JavaScript You Must Know -- Essential Syntax__.*

---

Among the useful features that have been added to JavaScript is this: `...`

That's right, three dots one after the other. These three dots can be used in two ways:
* spread operator
* rest parameters

## Spread Operator

The spread operator, as the name suggests, "spreads out" the items of an iterable (like an array) into individual elements.

```js
const randomNumbers = [2, 9, 17];

console.log(randomNumbers); // output: [2, 9, 17]
console.log(...randomNumbers); // output: 2 9 17
```

Notice that, when using the spread operator, the output is no longer an array but rather the individual elements of the array.

There is more to be discussed regarding the spread operator and arrays (and objects). But before that, let me just mention here that the spread operator can also be used on strings as well. In the case of a string, using the spread operator will simply return the individual characters of the string.

```js
const randomString = "defiance";

console.log(...randomString); // output: "d" "e" "f" "i" "a" "n" "c" "e"
```

Now back to arrays (and objects). First things first, the spread operator can't be used on objects without a surrounding context. For example, the following code will generate an error:

```js
const myObj = { a: "hello", b: "world" };

console.log(...myObj); // this will generate an error
```

The cool thing about the spread operator is that when it is used within the context of an array/object (meaning used inside square brackets or curly braces), it results in a new array/object containing the elements of the original. Examples are in order.

In the code example below, the spread operator is being used within the context of an array (inside square brackets). First a copy of the array `randomNumbers` is created using the spread operator and then a new array is created using `randomNumbers`.

```js
const randomNumbers = [2, 9, 17];
const randomNumbersCopy = [...randomNumbers];
const randomNumbersExtended = [-1, ...randomNumbers];

console.log(randomNumbersCopy); // output: [2, 9, 17]
console.log(randomNumbersExtended); // output: [-1, 2, 9, 17]
```

The spread operator can also be used to combine multiple arrays to create a new array.

```js
const upperBody = ["head", "shoulders"];
const lowerBody = ["knees", "toes"];
const poem = [...upperBody, ...lowerBody];

console.log(poem); // output: ["head", "shoulder", "knees", "toes"]
```

The same techniques we just saw in the two examples above can also be used for objects.

```js
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };

const obj1Copy = {...obj1};
const objCombined = {...obj1, ...obj2};
const obj2Extended = {...obj2, e: 5};

console.log(obj1Copy); // output: { a: 1, b: 2}
console.log(objCombined); // output: { a: 1, b: 2, c: 3, d: 4 }
console.log(obj2Extended); // output: { c: 3, d: 4, e: 5 }
```

Now considering the example above, what would happen if I did something like this `const obj3 = { ...obj1, b: 31415 }`? The catch here is that `obj1` already has a key called `b` and duplicates are not allowed. In this case, the resulting object will have the updated value of `b`.

```js
const obj1 = { a: 1, b: 2 };
const obj3 = { ...obj1, b: 31415 };

console.log(obj3); // output: { a: 1, b: 31415 }
```

This is a nifty trick to have in your arsenal if you ever wish to update just one of the properties of an object while keeping the rest untouched.

Let's do a relatively practical example. Say we have a function that takes exactly three arguments and returns their average. Also, say we have an array of three numbers and wish to know their average.

```js
function average(a, b, c) {
  return (a + b + c) / 3;
}

const randomNumbers = [3, 5, 7];
/**
 * Instead of this:
 * const avg = average(randomNumbers[0], randomNumbers[1], randomNumbers[2]);
 * we can simply use the spread operator here
 **/
const avg = average(...randomNumbers);

console.log(avg); // output: 5
```

Another practical example would be the `Math.max()` method. `Math.max()` returns the largest number in a list of numbers but does not accept an array as its argument. Rather it expects a list of numbers.

```js
const epicListOfNumbers = [1, 2002, 4, 91, 104, 7];

console.log(Math.max(...epicListOfNumbers)); // output: 2002
```

## Rest Parameters

Rest parameters does the exact opposite of the spread operator i.e. it expects a list of numbers and creates an array out of those numbers.

```js
function uselessFunction(a, b, ...rest) {
  console.log(a);
  console.log(b);
  console.log(rest);
}

uselessFunction("first argument", "second argument", "third", "fourth", "and", "sixth argument");
```

> [console output]:
>
  > ```
  > "first argument"
  > "second argument"
  > ["third", "fourth", "and", "sixth argument"]
  > ```
> 

So the parameter `a` of `uselessFunction()` receives `"first argument"` and the parameter `b` receives `"second argument"`; nothing new here. But, as can be seen in the console output, all the remaining arguments are received by `rest` as an array. That only happened because of the `...` that precedes the parameter `rest` (the three dots would be called the **rest syntax** in this case). If the rest syntax had not been there, then the argument `"third"` would've been received by the parameter `rest` and the remaining arguments would've simply been ignored.

```js
function uselessFunction(a, b, rest) {
  console.log(a);
  console.log(b);
  console.log(rest);
}

uselessFunction("first argument", "second argument", "third", "fourth", "and", "sixth argument");
```

> [console output]
>
  > ```
  > "first argument"
  > "second argument"
  > "third"
  > ```
> 

In this way, rest parameters allow us to have an indefinite number of arguments. One point to note here is that the rest parameters give us an actual array. So all the `Array` methods (like `map()` and `reduce()`) are available to us.

> ❗️important
>
> The rest parameters must be the last in the list of parameters. Both of the following function declarations will raise an error:
>
  > `function restMustBeLast(a, ...rest, b) { // -- } // error`
  >
  > `function restMustBeLast(...rest, a, b) { // == } // error`
  >
>
> The correct way to declare this function would be:
>
  > `function restMustBeLast(a, b, ...rest) { // -- }`
  >
> 

Let's revisit the `average()` function we wrote near the end of the **spread operator** section and improve it. As we programmed it before, `average()` took exactly three numbers and gave us their average. That is quite limited. Ideally we should be able to give an arbitrary long list of numbers and be able to get their average.

```js
function average(...numbers) {
  const sumOfNumbers = numbers.reduce((accumulator, currentValue) => (accumulator + currentValue));
  const avgOfNumbers = sumOfNumbers / numbers.length;

  return avgOfNumbers;
}

console.log(average(1, 2, 3, 4, 5)); // output: 3
console.log(average(1, 2, 3, 4, 5, 6)); // output: 3.5
console.log(average(1, 2, 3, 4, 5, 6, 7, 8, 9)); // output: 5
```

> 💡just a tip
>
> There is an array-like object called `arguments` which gives us access to all of the arguments passed to a function (except arrow functions which do not have `arguments`). An indefinite argument list was handled using this `arguments` variable before the **rest parameters** were made available in JavaScript. An important point to remember, though, is that `arguments` is not an actual array, so the `Array` methods are not available on it.
>

In this section we looked at the **rest syntax** in the context of a function's *parameter list*. In this context, the correct term to use would be *rest parameters*. The same syntax can also be used in **destructuring** too as we will see when we discuss destructuring insha'Allah.

---

👉🏻 Subscribe to my newsletter: [click here](https://www.bracketsinstitute.com/newsletter-subscription)

👉🏻 Follow me on twitter: [click here](https://twitter.com/therealnrf)

---