Monday, November 4, 2024

The Revolutionary ES6 Rest and Spread Operators

JavaScript Examples

Introduced in 2015, ES6 (also known as ECMAScript 2015 or ECMAScript 6) was the most impactful revision of JavaScript since ES5 (ECMAScript 5) in 2009. It added a veritable cornucopia of features, including Arrow Functions, the const and let keywords, default parameters, among others. Then ECMAScript 2016, 2017, and 2018 added even more, including the somewhat enigmatic Rest parameters and Spread operator (or Syntax, if you like). Part of the 2018 revision, the Spread operator is really two operators that share the same ellipsis syntax (). The purpose of this web development tutorial will be to give you a taste of what these operators have to offer while showing how to differentiate between the two. As we will soon see, it is all a matter of context!

Read: Best Online Courses to Learn HTML

Rest Parameters and Variadic Functions in JavaScript

You may never have heard of variadic functions before, but there is a good chance that you have run across at least one. Simply put, a variadic function is one which accepts a variable number of arguments. In pre-ES6 JavaScript (JS), we would often apply an array method, like slice, to the local arguments object in order to convert it to a true array, which we could then process.

Now, we can declare a Rest parameter inside the function signature to accomplish the same thing. Moreover, as stated in the MDN Web Docs:

“The arguments object is not a real array, while rest parameters are Array instances, meaning methods like sort, map, forEach or pop can be applied on it directly.”

Here are two implementations of a sum() function – one using ES5 JS and the other ES6:

// Prior to ES6
function sum(/* var1, var2, var3, var4, var5, etc... */) {
  return Array.prototype.reduce.call(arguments, function(a, b) {
    return a + b;
  }, 0);
}

// Using a ES6 Rest Parameter
const sum = (...args) => args.reduce((a, b) => a + b, 0);

Combining the Rest Parameter with Named Arguments in ES6

Looking at our sum function, it should be immediately apparent that it requires at least two arguments to add together. We can give the function a couple of named arguments for those and then handle the rest using a Rest parameter:

const sumWithNamedArguments = (num1, num2, ...additionalArgs) =>   
  additionalArgs.reduce((a, b) => a + b, num1 + num2);

It is called a Rest parameter because it is always used for the all arguments after any named ones. Hence:

  • A function definition can have only one Rest parameter
  • it must ALWAYS be the last function argument.

Placing it anywhere else will result in an error such as:

Uncaught SyntaxError: Rest parameter must be last formal parameter 

What Is the Spread Operator and How Does It Differ from the Rest Parameter?

The ES6 Spread operator shares the exact same syntax as the Rest parameter, but achieves almost the opposite result. While the Rest parameter collects all the remaining function arguments into an array, the Spread operator expands iterables into individual elements. In fact, the Spread syntax works almost anywhere except inside of function definitions, such as within array literals, function calls, and initialized property objects to spread the values of iterable objects into separate items.

Some examples of the Spread operator in each of the above applications will help put everything into sharp focus.

Spread Example in JavaScript: In an Array Literal

Applying the Spread syntax to an array within an array literal will result in the target array being shallow copied into the new one as new array elements:

const toppings   = ["pepperoni", "mushrooms", "green peppers", "onions"];
const pizzaOrder = ["medium", "thin crust", "pizza with", ...toppings, "to go"];

// Displays [ "medium", "thin crust", "pizza with", 
// "pepperoni", "mushrooms" "green peppers", "onions", "to go"]
console.log(pizzaOrder);

Had we not employed the Spread operator, the pizzaOrder array would contain five elements, with the fourth element being another array.

Spread Example in JavaScript: In a Function Call

Remember the sum function from earlier? Whereas the Rest parameter collected the passed arguments within the function definition, the Spread operator can be utilized to expand an array’s elements into arguments when invoking a function:

const sum = (...args) => args.reduce((a, b) => a + b, 0);

const numbers = [3, 4, 5, 7];
// Displays 20
console.log(sum(...numbers));

Spread Example in JavaScript: On Initialized Property Objects

In the code example below, we are using Spread inside the pizzaOrder2 object to expand the toppings and pizzaType array elements into individual properties in the same way that Array.concat() would:

const toppings    = ["pepperoni", "mushrooms", "green peppers"];
const pizzaType   = ["medium", "thin crust", "pizza"];
const pizzaOrder2 = {...toppings, location: "to go", ...pizzaType };
console.log(pizzaOrder2);

console.log(pizzaOrder);
{
  "0": "medium",
  "1": "thin crust",
  "2": "pizza",
  "location": "to go"
}

Read: Top Online Courses to Learn WordPress

Additional Examples of the Spread Operator in JavaScript

The Spread operator can be used for a variety of purposes, some intuitive, others that you may not have thought of.

Converting a JavaScript String into Individual Array Elements

Applying the Spread syntax to a string will convert into an array with each character occupying its own element:

const myName = "Rob Gravelle";

console.log([...myName]);

// Displays:
[ "R", "o", "b", "", "G", "a", "v", "e", "l", "l", "e" ]

Creating a Shallow Copy of an Array or Object in ES6

An easy way to make a shallow copy of an array or object is to create a new one with the Spread syntax applied to the original:

let colors = ['Black', 'Red', 'Green'];
let newColors = [...colors];
// Displays false
console.log (newColors == colors);

Choosing Between Two Method Invocations in JS

Need to call a method with different arguments based on some condition? Combining the Spread operator with the ternary operator gives us a simple and elegant way to feed the method with the correct number of arguments within the same statement:

const person = {
    name: 'Rob',
    location: {
      city: 'Ottawa',
      country: 'Canada',
      phoneNumbers: ['555-1234', '555-2345'],
    },
  };

const printJson = (prettyPrint: boolean) => {
  document.getElementById('output').innerText = 
    JSON.stringify(...prettyPrint ? [person, null, '  '] : [person]);
}
printJson(true);
/* Displays:
{
  "name": "Rob",
  "location": {
    "city": "Ottawa",
    "country": "Canada",
    "phoneNumbers": [
      "555-1234",
      "555-2345"
    ]
  }
}
*/

You’ll find all of today’s code samples in the codepen demo.

Final Thoughts on the Revolutionary ES6 Rest and Spread Operators

This tutorial provided an overview of the enigmatic ES6 Rest and Spread operators and outlined when to use one versus the other. The key is to remember that each achieves the almost opposite result of the other: the Rest parameter collects all the remaining function arguments into an array, while the Spread operator expands iterables into individual elements.

Read more JavaScript programming and web development tutorials.

Rob Gravelle
Rob Gravelle
Rob Gravelle resides in Ottawa, Canada, and has been an IT guru for over 20 years. In that time, Rob has built systems for intelligence-related organizations such as Canada Border Services and various commercial businesses. In his spare time, Rob has become an accomplished music artist with several CDs and digital releases to his credit.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured