The JS Spread (or Gather!) Operator: A Rundown

The JS Spread (or Gather!) Operator: A Rundown

Note: this article is intended to be a gateway to future articles that I want to write - specifically destructuring, decomposition, and (in the future) functional programming. I want those who stumble upon my blog to be able to read up on some of the building blocks of the path I want to go down with these articles, and the spread operator is an important (and interesting!) step in that regard.

JS's spread (...) operator is one of the most useful and powerful operators that came along in ES6. In this article (and the destructuring article I'll be writing after!), I will strive to give a good rundown of the operator, and hopefully teach you something new along the way!

Pop Quiz

Before continuing on, do your best to figure out each of the console.logs in the following code:

function spreadArgs(...args) {
    console.log(args);
}

spreadArgs(1, 2, 3); // Output 1;


function spreadOutput(...args) {
    console.log(...args);
}

spreadOutput(1, 2, 3); // Output 2;


function restArgs(one, two, ...args) {
    console.log(one, two, args);
}

restArgs(1, 2, 3, 4, 5); // Output 3;

Done! The answers (in order), are:

  1. [1, 2, 3]
  2. 1 2 3
  3. 1 2 [3, 4, 5]

If these answers weren't what you were expecting, take a second and see if you can figure out why, and then read on!

The Basics

When used in the context of a function's expected parameters, the spread (...) operator is called rest syntax, or rest parameters. This is because it gives you the rest of the parameters (well, arguments technically).

Note: Arguments are the values passed in to a function, parameters are the names assigned to those values.

In Output 1, our spreadArgs function looks like it takes a single parameter called args. If you haven't used rest syntax before, then you may think the output would just be 1. However, when used in parameter assignment like this, the rest syntax gathers all the arguments together into an array. That's why the output is [1, 2, 3] - we simply bundled (gathered!) together all the arguments that were passed in and put them in an array. I agree with Kyle Simpson's opinion that the spread/rest operator should, in assignment contexts, be called the gather operator, because that's really what it's doing.

Let's look at our second output, our function spreadOutput. This function uses rest syntax on the arguments again, and we now know what to expect from that. However, the output is very different, because we use the spread operator (in its natural environment!) on the output itself. The parameter args is indeed [1, 2, 3], but when we spread that array out, it becomes three independent values, just like they were passed in.

This is where the primary difference of the spread vs rest syntax can be seen. In an assignment context (the parameter args, because we're assigning the arguments to it), we gather the values together into an array. In an "output" or (value-list) context, such as console.log(...args);, the values are spread apart into individual values.

Hopefully you now understand our third function - restArgs. If you don't, don't worry! We'll walk through it now:

restArgs has three parameters, but we pass in 5 arguments to it. The value 1 gets assigned to the parameter one, and 2 gets assigned to two. What happens to the rest (!) of the arguments? Thanks to the rest syntax (gather operator), they get bundled up into an array ([3, 4, 5]). In our console.log, we don't spread args back out, hence our output of the two independent values (1 and 2) and the array ([3, 4, 5]). If we changed our console log to console.log(one, two, ...args)) our output would become 1 2 3 4 5.

There are some important things to note as far as the placement of the spread operator when you use it. For example, what do you think would happen in the following block?

function what(one, ...args, two) {
    console.log(one, args, two);
}

what(1, 2, 3, 4); // What happens here?

Without running this code, think through the steps. The argument 1 gets assigned to parameter one. And then... we gather the rest of the arguments together into an array ([2, 3, 4]). So what happens to our poor two parameter? We'll actually never know, because if you run this code, you will get this: Uncaught SyntaxError: Rest parameter must be last formal parameter

Hopefully, this should make sense to you. If you have a rest parameter before other parameters, what arguments are supposed to be assigned to those following parameters? All the arguments have been gathered up and happily placed into an array by the rest parameter. The same would happen if you instead did function what(...args, one, two), naturally.

This, fortunately, doesn't happen in an output/value-list context. Let's say we have our original restArgs function declaration, but change the console.log:

function restArgs(one, two, ...args) {
    console.log(...args, one, two);
}

Then we call it with restArgs(1, 2, 3, 4, 5);. Our output will be 3 4 5 1 2, instead of a syntax error. This is because args gets spread out into the individual values, instead of the values all being gathered up.

Conclusion

The spread (or gather) operator has many applications and usages we haven't even touched here, but the basics described above are crucial in building the foundation of our understanding of the operator so that the further applications (such as destructuring!) make more sense intuitively. Even though this article is a repeat of many other resources out there, I hope that I at least helped solidify your knowledge of the operator, and I'd love to hear your feedback.

You can find a CodePen with the code used in this article here.