Rest and Spread – Things you can do with just 3 dots in Modern JavaScript!

function withNoArgument() {
  console.log("no isses");
}
withNoArgument("Asif", "Dev");

Before diving into rest and spread; let’s have some background info.
What do you think the output for the above code?
Yes, you guessed it right. JavaScript will not throw any error when we call a parameter-less function with any number of parameter and it will output ‘no issues‘.

As such; the following code is also valid:

function sum(a, b) {
  return a + b;
}

alert( sum(1, 2, 3, 4, 5) );

There will be no error because of all the extra arguments but in the result only the first two will be counted.

Rest Operator

The rest of the parameters can be included in the function definition by using three dotsfollowed by the name of the array that will contain them. The dots literally mean “gather the remaining parameters into an array”. For example:

function restExample (a, b, ...restOfThem) {
  console.log(restOfThem.length);
}
restExample(25, 23, 2, 11, 8, 17, 6);

This code assigns 25 to a, 23 to b and [2, 11, 8, 17, 6] to the variable restOfThem.

The …rest must always be last. The rest parameters gather all remaining arguments, so the following does not make sense and causes an error:

function f(arg1, ...rest, arg2) { // arg2 after ...rest
  // error
}

There is also a special array-like object named arguments that contains all arguments by their index. Let’s get back to the first example –

function withNoArgument() {
  console.log(arguments[0]);
 // asif
  console.log(arguments[1]);
 // dev
}
withNoArgument("Asif", "Dev");

When rest parameters did not exist in the language, using arguments was the only way to get all arguments of the function. But the downside is that although arguments is both array-like and iterable, it’s not an array. It does not support array methods, so we can’t call arguments.map(…) for example. Also, it always contains all arguments. We can’t capture them partially, like we did with rest parameters.

Spread Operator

Rest parameters is a way to get an array from the list of parameters. But sometimes we need to do exactly the reverse. For instance, there’s a built-in function Math.max that returns the greatest number from a list. Now let’s say we have an array [3, 5, 1]. How do we call Math.max with it? Passing it “as is” won’t work, because Math.max expects a list of numeric arguments, not a single array. And we can’t manually list items in the code, because we may be unsure how many there are. As our script executes, there could be a lot, or there could be none.

Spread syntax to the rescue! It looks similar to rest parameters, also using , but does quite the opposite. When …arr is used in the function call, it “expands” an iterable object arr into the list of arguments. Like –

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(...arr1, ...arr2) ); // 8

We can even combine the spread syntax with normal values. Also, the spread syntax can be used to merge arrays.

We can use spread operator to easily copy array or object to another array or object.

let arr = [1, 2, 3];
let arrCopy = [...arr]; // spread the array into a list of parameters
                        // then put the result into a new array

// do the arrays have the same contents?
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true

// are the arrays equal?
alert(arr === arrCopy); // false (not same reference)

// modifying our initial array does not modify the copy:
arr.push(4);
alert(arr); // 1, 2, 3, 4
alert(arrCopy); // 1, 2, 3

But why can’t we just write let arrCopy = arr? The answer is, Javascript arrays and objects are passed by reference, not by value. This means that when we write let arrCopy = arr, our new variable arrCopy isn’t actually a copy of arr — it’s a reference that points to arr. So, if we change arr, then arrCopy changes as well.

Suppose we want a new array with all the contents of arr, except now with a new element at the end. We can just use array.push(), assign to the new variable and be done with it; right? Wrong! Because array.push() will just alter both array; which defeats the purpose. So what to do? Yes; spread to the rescue!

let arr = [1, 2, 3];
let newArray = [...arr, 4];
Conclusion

Rest and spread are important inclusion in modern JavaScript. The rest operator (…) allows us to call a function with any number of arguments and then access those excess arguments as an array. The rest operator also allows us in destructuring array or objects. The spread operator (…) allows us to expand an iterable like array into its individual elements. ... could be used to represent either a spread operator or a rest parameter. How do we tell the difference? Well it entirely depends on how we use it. Given the context in which we use the three dots, it is easy to tell whether we are using it as a rest parameter or a spread operator. There’s an easy way to distinguish between them:

  • When … is at the end of function parameters, it’s “rest parameters” and gathers the rest of the list of arguments into an array.
  • When … occurs in a function call or alike, it’s called a “spread syntax” and expands an array into a list.


Use patterns:

  • Rest parameters are used to create functions that accept any number of arguments.
  • The spread syntax is used to pass an array to functions that normally require a list of many arguments.

Together they help to travel between a list and an array of parameters with ease.