This article will cover the spread operator and how you can use them in your day-to-day JavaScript programming. This article assumes you have some familiarity with coding in the JavaScript ecosystem.

Prerequisites

We will be using a code example from a previous post - Rest Parameters in Javascript. Since the spread operator does the opposite thing of what rest parameters do, a good understanding of rest parameters would be helpful here. However, as a brief introduction, using the three periods syntax (…), rest parameters allow us to capture an indefinite number of function arguments as an array. It is kind of similar to Java Varargs or C# parameter arrays.

In this code example, the log function takes the level and args parameters then outputs them to the console.

// ------------------ 👇 a rest parameter
function log(level, ...args) {
  for (var i = 0; i < args.length; i++) {
    console.log(level, `{$i}`, args[i]);
  }
}



The Spread Operator or Spread Syntax

Basically, the spread operator converts an array to a list of comma-separated values (CSV), contrasting rest parameters, where it takes a CSV and converts it into an array.

We have below a logging function similar to the one above (but without using rest parameters). Let us assume that the responseBody variable value comes from an external API that returns a list with varying types and values, and we want to log each element to the console. We can see from the output that while the function is expecting four arguments, we’ve only given it two. Hence the undefined being shown twice for variables arg2 and arg3.

function log(level, arg1, arg2, arg3) {
  console.log(level, '{0}', arg1);
  console.log(level, '{1}', arg2);
  console.log(level, '{2}', arg3);
}

var responseBody = [
  {name: 'Non-Master Anakin Skywalker'},
  1,
  false 
];

log('DEBUG', responseBody);
// expected output:
// DEBUG {0} [ { name: 'Non-Master Anakin Skywalker' }, 1, false ]
// DEBUG {1} undefined
// DEBUG {2} undefined


This can be resolved by providing the correct arguments to the function. This can be done by getting each element by index and pass each to the function.

log('DEBUG', responseBody[0], responseBody[1], responseBody[2]);
// expected output:
// DEBUG {0} { name: 'Non-Master Anakin Skywalker' }
// DEBUG {1} 1
// DEBUG {2} false


That should be good, our logging function is working as expected, but this can be improved. The desired output can be done with less code using the spread operator. We don’t need to access each list element by index. We can use the three periods syntax (…) again here and prepend it to the list variable. Converting it to a comma-separated values that the logging function is expecting.

// --------- 👇 spread operator
log('DEBUG', ...responseBody);
// expected output:
// DEBUG {0} { name: 'Non-Master Anakin Skywalker' }
// DEBUG {1} 1
// DEBUG {2} false


What was done by using the spread syntax was taking a list and converting it to a CSV. When we used the logging function at first, we missed providing the remaining two arguments. As if we were calling the function like this:

log('DEBUG', [{name: 'Non-Master Anakin Skywalker'}, 1, false], undefined, undefined);


Using the spread operator, we have correctly provided the logging function’s arguments. Spreading the value of the list into an actual CSV that is accepted by the function. As if we were calling the function like this, which is the correct usage:

log('DEBUG', {name: 'Non-Master Anakin Skywalker'}, 1, false);



Spread Operator and Rest Parameters

If you’re already comfortable with the concept of the spread operator, let us go back to my previous point where it is conceptually the opposite of rest parameters. Let’s use the first logging function from above that uses rest parameters and use it together with the spread operator.

When directly passing the list variable, it shows only one debug output to the console. This is expected behavior since what rest parameters do is capture an indefinite number of arguments supplied to a function. In this case, we’ve only given it one argument, thus logging only once.

function log(level, ...args) {
  for (var i = 0; i < args.length; i++) {
    console.log(level, `{${i}}`, args[i]);
  }
}

var responseBody = [
  {name: 'Non-Master Anakin Skywalker'},
  1,
  false 
];

log('DEBUG', responseBody);
// expected output:
// DEBUG {0} [{ name: 'Non-Master Anakin Skywalker' }, 1, false]


When passing the list variable using the spread syntax to the function, it shows three debug console outputs, which is the expected output, matching the number of elements from the list.

log('DEBUG', ...responseBody);
// expected output:
// DEBUG {0} { name: 'Non-Master Anakin Skywalker' }
// DEBUG {1} 1
// DEBUG {2} false

This shows how rest parameters and the spread operator are opposite of each other and how the two can be used together.

Let us look at another example of the spread operator but with a list with four elements. This time it will output to the console four times.

var responseBody = [
  {name: 'Non-Master Anakin Skywalker'},
  1,
  false,
  ['Tatooine', 'Coruscant']
];

log('DEBUG', ...responseBody);
// expected output:
// DEBUG {0} { name: 'Non-Master Anakin Skywalker' }
// DEBUG {1} 1
// DEBUG {2} false
// DEBUG {3} [ 'Tatooine', 'Coruscant' ]



Adding Element to a List

There is also another use for the spread operator aside from passing them as function arguments.

Here we have a list variable tail that has two elements. If we want to add a new element at the end of the list, that should be straightforward.

const head = {value: 'A'};
const tail = [{value: 'B'}, {value: 'C'}];
console.log(tail);
// expected output:
// [ { value: 'B' }, { value: 'C' } ]

tail.push(head);
console.log(tail);
// expected output:
// [ { value: 'B' }, { value: 'C' }, { value: 'A' } ]


When adding a new element at the front of the list, we can use the spread operator.

const head = {value: 'A'};
const tail = [{value: 'B'}, {value: 'C'}];
// -------------------- 👇 spread operator
const newList = [head, ...tail]

console.log(newList);
// expected output:
// [ { value: 'A' }, { value: 'B' }, { value: 'C' } ]



End

In this post, we looked at the spread operator in order to convert a list into a CSV. We learned how the spread operator is the opposite of rest parameters and how they can be used together. Then, we concluded by using the spread operator to append elements at the front of a list.