ES6 arrow functions, syntax and lexical scoping
ES2015 (ES6) introduces a really nice feature that punches above its weight in terms of simplicity to integrate versus time saving and feature output. This feature is the arrow function.
Before we dive into the features of the arrow function and what it actually does for us, let’s understand what an arrow function is not. It’s not a replacement for the
function
keyword, at all. This means you can’t do a find and replace on every single function
keyword and everything works perfectly, because it likely won’t.
Table of contents
If you’re competent with the way JavaScript scope works, and have a great understanding of lexical scope, the
this
keyword and Prototype methods such as .call()
, .apply()
and .bind()
, then you’re in good hands to continue reading.Syntax
Let’s look at what the arrow function’s construct is from MDN:
The “normal JavaScript” (ES5) equivalents to help transition:
The ES6 and ES5 differences in
example 1
are that the function
keyword is omitted, and =>
now exists after the arguments. In example 2
, our function has been reduced to one line, this is great for single line function expressions that get return
‘d.Hint: arrows are anonymous
Arrow functions are always anonymous, which means we can’t do this with ES6:
Instead of this, we could assign our anonymous arrow function it to a variable (using
var
here instead of let
as ES6 block scoping is another topic):
Let’s look at the syntaxes a little further and then the functionality differences when using arrow functions.
Syntax: single line expressions
We touched briefly above on single line expressions, let’s look at a great use case for them.
Let’s take some junky ES5 example that iterates over an Array using
Array.prototype.map
:
We can reduce this down to a single line with an arrow function, which saves us a lot of typing and can actually enhance readability in my opinion as this piece of code has one clear role:
Syntax: single argument functions
Arrow functions also give us a small “sugar” syntax that allows us to remove parenthesis when only using a single argument in a function.
Taking the last piece of code for example we had this:
When we could remove the parens from
(number)
to leave us with this:
This is great and a little clearer initially, but as we all know applications grow and code scales, and to save us headaches (be it forgetting syntaxes or lesser experienced developers “not knowing” to add parens back with more than one argument), I’d recommend always using the parens out of habit, even for single args:
Functionality: lexical scoping “this”
Now we’re past the sugar syntax excitement, we can dig into the benefits of the arrow function and its implications on execution context.
Typically if we’re writing ES5, we’ll use something like
Function.prototype.bind
to grab the this
value from another scope to change a function’s execution context. This will mainly be used in callbacks inside a different scope.
In Angular, I adopt the
controllerAs
syntax which allows me to use this
inside the Controller to refer to itself (so here’s an example). Inside a function the this
value may change, so I could have a few options, use that = this
or .bind
:
The
this.foo = response;
won’t work correctly as it’s been executed in a different context. To change this we could use .bind(this)
to give our desired effect:
Or you may be used to keeping a top level
this
reference, which can make more sense when dealing with many nested contexts, we don’t want a gross tree of .bind(this), .bind(this), .bind(this)
and a tonne of wasted time binding those new functions (.bind
is very slow). So we could look at that = this
to save the day:
With arrow functions, we have a better option, which allows us to “inherit” the scope we’re in if needed. Which means if we changed our initial example to the following, the
this
value would be bound correctly:
We could then refactor some more into a nice single line expression, push to git and head home for the day:
The interesting thing to note is that the
this
value (internally) is not actually bound to the arrow function. Normal functions in JavaScript bind their own this
value, however the this
value used in arrow functions is actually fetched lexically from the scope it sits inside. It has no this
, so when you use this
you’re talking to the outer scope.
Comments
Post a Comment