Everyday javascript ep. 1: Rest/spread operator pt1.
Length: 15 minutes.
What's this about?
Going trough the internet you'll find a lot of in-depth articles about specific issues, light tutorials, and everything in between. But the javascript landscape is vast, and sometimes too much information gets in the way of understanding.
Even after years of professional development I see tricks every now and then which are straightforward conceptually, but easy to miss, and there's no place where I could read more about real usage, and not a 30 page long detailed article about every small detail of a given feature.
The goal of this article series is to show the tricks used in real-world javascript/typescript, aimed at programmers from an intermediate level to advanced. But don't be afraid if you're a beginner, I think you can understand everything written here, but in my experience it's better to experience a problem and then find a solution.
TLDR:
- How does the rest/spread operator looks like
- Spread/rest arrays, what it solves, with problems and solutions
- Spread/rest with objects, in a similar fashion
The following variables are used in the examples:
The rest/spread operator
The rest/spread operator looks like this: ...
. It is always on a left side of an identifier ( a name for a variable etc... ),
otherwise it would be easily confused with property access on objects ( plus 2..toString()
is a valid JS expression,
dangerously close to three dots ).
It was introduced with es6, which was quite a while ago. As with every new feature it solves a problem that's solvable with old tools ( there will be examples of that, don't worry ), but using it simpifies code a lot, making it way more readable. One common thing with a lot of cool js features is that they can be used to solve a lot of different, seemingly unrelated problems, and the result is less head space occupied by syntax and more with code.
ES6 really moved javascript in a functional direction, almost all of the additions helps the programmer to
write immutable code, which means never mutating variables, but creating new ones whenever they needed. No more Array.prototype.push
.
Code written in this manner is way easier to read and reason about.
It can be a confusing thing to use the same operator for two different things. Obviously there's a rule which specifies that, but in everyday coding it's more intuitive. A good rule of thumb is the following:
- If the
...
operator is on a "left" side of an expression, or in a function definition's arguments, then it's a rest - If it's on the right side of an expression or in a function calls argument, then it is a spread
For example:
I'll use the following variables in the examples:
Spread operator on arrays
The spread operator is commonly used in two places, always before an array variable:
- at a variable in an argument list of a function
- in a newly created array, between
[
and]
, at a array variable
Let's see some examples for the first part, with-and-without:
You can see some of the benefits right away:
- Simpler code, no need to know about
this
and the correct arguments forapply
, you don't have to remember ifslice()
modifies the existing array ( its not, it is used to make a copy ), you won't mutateargumentList
and use it later without remembering etc... I'm not going to make this statement every time, I promise, but only because I don't like repeating myself.
Let's see the another option, in creating a new array:
Again, you don't have to remember any built-in functions.
If you see it for the first time then it can get confusing for a moment, but after that period it's very easy to read.
You can also concat multiple arrays, with elements between arrays:
The line above prints out the fibonacci numbers up to 13
.
Rest operator on arrays
The rest is used
- at an argument in a function definition
- at variable declaration, between
[
and]
The first one is mainly used for functions which have a variable length argument list, meaning you can pass as many
arguments as one ( for ex. console.log
is such a function, try console.log("1","2","3")
).
Let's see a simple example, which removes the first item from an array, prints it on the console, and returns the sum of the remaining items:
Where does arguments
in the old example comes from? It exists in every function, and it is an Array-like object,
meaning that sometimes it behaves like an array, sometimes it does not. It does not for example have a .reduce
method.
You have to create a new array ( from which you have to pop out a member ) from arguments
, which then you can use.
As you can see you have to remember a lot of things again, just to solve a relatively simple problem. The bonus is that in a good IDE ( let's say Webstorm ) you get much better type hints.
One thing to remember that in an argument list the rest operator must be at the last argument. You can't do this:
One way to think of this is that a javascript function has infinite arguments, they are just undefined :). You don't know, and neither the computer where it "ends". Or just get used to it.
For the variable declaration:
Again, numberArr is mutated. Of course you can get around the mutation issue even with "old" JS, but it will be messier.
Usage with object is coming up in the next part!