this
that = this
this
bind(this)
bind()
this
via parameters=>
) in ES6, but no “thin” arrow functions (->
)?There are two benefits to arrow functions.
First, they are less verbose than traditional function expressions:
Second, their this
is picked up from surroundings (lexical). Therefore, you don’t need bind()
or that = this
, anymore.
The following variables are all lexical inside arrow functions:
arguments
super
this
new.target
this
In JavaScript, traditional functions can be used as:
These roles clash: Due to roles 2 and 3, functions always have their own this
. But that prevents you from accessing the this
of, e.g., a surrounding method from inside a callback (role 1).
You can see that in the following ES5 code:
In line C, we’d like to access this.prefix
, but can’t, because the this
of the function from line B shadows the this
of the method from line A. In strict mode, this
is undefined
in non-method functions, which is why we get an error if we use Prefixer
:
There are three ways to work around this problem in ECMAScript 5.
that = this
You can assign this
to a variable that isn’t shadowed. That’s what’s done in line A, below:
Now Prefixer
works as expected:
this
A few Array methods have an extra parameter for specifying the value that this
should have when invoking the callback. That’s the last parameter in line A, below.
bind(this)
You can use the method bind()
to convert a function whose this
is determined by how it is called (via call()
, a function call, a method call, etc.) to a function whose this
is always the same fixed value. That’s what we are doing in line A, below.
Arrow functions work much like solution 3. However, it’s best to think of them as a new kind of functions that don’t lexically shadow this
. That is, they are different from normal functions (you could even say that they do less). They are not normal functions plus binding.
With an arrow function, the code looks as follows.
To fully ES6-ify the code, you’d use a class and a more compact variant of arrow functions:
In line A we save a few characters by tweaking two parts of the arrow function:
The “fat” arrow =>
(as opposed to the thin arrow ->
) was chosen to be compatible with CoffeeScript, whose fat arrow functions are very similar.
Specifying parameters:
Specifying a body:
The statement block behaves like a normal function body. For example, you need return
to give back a value. With an expression body, the expression is always implicitly returned.
Note how much an arrow function with an expression body can reduce verbosity. Compare:
Omitting the parentheses around the parameters is only possible if they consist of a single identifier:
As soon as there is anything else, you have to type the parentheses, even if there is only a single parameter. For example, you need parens if you destructure a single parameter:
And you need parens if a single parameter has a default value (undefined
triggers the default value!):
The following are two ways in which the values of variables can be propagated.
First, statically (lexically): Where a variable is accessible is determined by the structure of the program. Variables declared in a scope are accessible in all scopes nested inside it (unless shadowed). For example:
Second, dynamically: Variable values can be propagated via function calls. For example:
The source of this
is an important distinguishing aspect of arrow functions:
this
; its value is determined by how they are called.this
; its value is determined by the surrounding scope.The complete list of variables whose values are determined lexically is:
arguments
super
this
new.target
There are a few syntax-related details that can sometimes trip you up.
If you view =>
as an operator, you could say that it has a low precedence, that it binds loosely. That means that if it is in conflict with other operators, they usually win.
The reason for that is to allow an expression body to “stick together”:
In other words, we want =>
to lose the fight against ===
and ?
. We want it to be interpreted as follows
If =>
won against both, it would look like this:
If =>
lost against ===
, but won against ?
, it would look like this:
As a consequence, you often have to wrap arrow functions in parentheses if they compete with other operators. For example:
On the flip side, you can use typeof
as an expression body without putting it in parens:
ES6 forbids a line break between the parameter definitions and the arrow of an arrow function:
Line breaks inside parameter definitions are OK:
The rationale for this restriction is that it keeps the options open w.r.t. “headless” arrow functions in the future (you’d be able to omit the parentheses when defining an arrow function with zero parameters).
Quick review (consult “Speaking JavaScript” for more information):
Expressions produce (are evaluated to) values. Examples:
Statements do things. Examples:
Most expressions1 can be used as statements, simply by mentioning them in statement positions:
If an expression is the body of an arrow function, you don’t need braces:
However, statements have to be put in braces:
Some parts of JavaScript’s syntax are ambiguous. Take, for example, the following code.
It could be:
bar
.bar
and the expression statement 123
.Given that the body of an arrow function can be either an expression or a statement, you have to put an object literal in parentheses if you want it to be an expression body:
For comparison, this is an arrow function whose body is a block:
Remember Immediately Invoked Function Expressions (IIFEs)? They look as follows and are used to simulate block-scoping and value-returning blocks in ECMAScript 5:
You can save a few characters if you use an Immediately Invoked Arrow Function (IIAF):
Similarly to IIFEs, you should terminate IIAFs with semicolons (or use an equivalent measure), to avoid two consecutive IIAFs being interpreted as a function call (the first one as the function, the second one as the parameter).
Even if the IIAF has a block body, you must wrap it in parentheses, because it can’t be (directly) function-called. The reason for this syntactic constraint is consistency with arrow functions whose bodies are expressions (as explained next).
As a consequence, the parentheses must be around the arrow function. In contrast, you have a choice with IIFEs – you can either put the parentheses around the whole expression:
Or just around the function expression:
Given how arrow functions work, the latter way of parenthesizing should be preferred from now on.
If you want to understand why you can’t invoke an arrow function by putting parentheses immediately after it, you have to examine how expression bodies work: parentheses after an expression body should be part of the expression, not an invocation of the whole arrow function. This has to do with arrow functions binding loosely, as explained in a previous section.
Let’s look at an example:
This should be interpreted as:
And not as:
Further reading: A section in the chapter on callable entities has more information on using IIFEs and IIAFs in ES6. Spoiler: you rarely need them, as ES6 often provides better alternatives.
bind()
ES6 arrow functions are often a compelling alternative to Function.prototype.bind()
.
If an extracted method is to work as a callback, you must specify a fixed this
, otherwise it will be invoked as a function (and this
will be undefined
or the global object). For example:
An alternative is to use an arrow function:
this
via parameters The following code demonstrates a neat trick: For some methods, you don’t need bind()
for a callback, because they let you specify the value of this
, via an additional parameter. filter()
is one such method:
However, this code is easier to understand if you use an arrow function:
bind()
enables you to do partial evaluation, you can create new functions by filling in parameters of an existing function:
Again, I find an arrow function easier to understand:
An arrow function is different from a normal function in only two ways:
arguments
, super
, this
, new.target
new
via the internal method [[Construct]]
and the property prototype
. Arrow functions have neither, which is why new (() => {})
throws an error.Apart from that, there are no observable differences between an arrow function and a normal function. For example, typeof
and instanceof
produce the same results:
Consult the chapter on callable entities for more information on when to use arrow functions and when to use traditional functions.
=>
) in ES6, but no “thin” arrow functions (->
)? ECMAScript 6 has syntax for functions with a lexical this
, so-called arrow functions. However, it does not have arrow syntax for functions with dynamic this
. That omission was deliberate; method definitions cover most of the use cases for thin arrows. If you really need dynamic this
, you can still use a traditional function expression.