Arrow Functions in JavaScript
Arrow functions were introduced in ES6 to work around several common gotchas with conventional functions. However, you still need to learn when to use conventional functions versus when to use arrow functions, because there are situations where using an arrow function is the wrong choice.
Syntax
When you see =>
, you're looking at an arrow function. There are
two ways to declare an arrow function:
- Without curly braces
{}
. With this syntax, the arrow function has an implicit return. For example, the below arrow function returns 42, even though there's noreturn
.
// 'getAnswer()` is an arrow function that returns 42
const getAnswer = () => 42;
getAnswer(); // 42
- With curly braces
{}
. With this syntax, the arrow function does not have an implicitreturn
.
// 'getAnswer()` is an arrow function that returns 42
const getAnswer = () => {
return 42;
};
getAnswer(); // 42
Returning an object literal from an arrow function is tricky:
// Syntax error! JavaScript assumes curly braces after `=>` means
// you're using the curly braces syntax
const getObj = () => { answer: 42 };
// With parentheses around the object literal, the below function
// correctly returns an object with `obj.answer = 42`
const getObj = () => ({ answer: 42 });
getObj(); // 42
Without curly braces, you can only put one expression to the right of the arrow =>
. Intuitively, this means you can only use the no curly brace syntax for "one-liners". You can use the ternary operator ?
, &&
, and ||
. But you cannot use if
statements or semicolons.
let answer = 42;
// Without curly braces, the arrow function can only contain one
// expression. The below function works fine, but you can't use
// an `if` statement without curly braces.
const getAnswer = () => answer !== null && answer !== undefined ?
answer :
0;
getAnswer(); // 42
Parameters
Like normal functions, arrow functions can take zero or more parameters.
You must put the parameter names in parentheses (param1, param2, param3) => {}
unless your arrow function takes exactly one parameter.
// If your arrow function takes no params, declare it with
// `() =>`
const getAnswer = () => 42;
// If your arrow function takes 1 param, you can omit the
// parentheses around the parameter names
let noop = v => v;
// Or, equivalently:
noop = (v) => v;
// If your arrow function takes more than 1 param, you must
// put parentheses around the parameter names
const add = (a, b) => a + b;
Why Arrow Functions?
Arrow functions have two major advantages:
- Implicit return for one-line functions means more concise code
- Lexical
this
.this
in the arrow function is the same asthis
outside the arrow function.
For example, suppose you try to call setTimeout()
in a
class method. If you use a normal function as opposed to
an arrow function, this
will not be an instance of MyClass
.
class MyClass {
constructor(message) {
this.message = message;
}
print() {
setTimeout(function() {
// undefined, because `this` is a `Timeout` object in
// a `setTimeout()` callback
this.message;
}, 100);
}
}
const obj = new MyClass('Hello, World');
obj.message; // 'Hello, World'
obj.print();
With an arrow function, this
will be an instance of MyClass
.
class MyClass {
constructor(message) {
this.message = message;
}
print() {
setTimeout(() => {
// 'Hello, World'
this.message;
}, 100);
}
}
const obj = new MyClass('Hello, World');
obj.message; // 'Hello, World'
obj.print();
Why Not Arrow Functions?
Arrow functions are excellent, and often it doesn't matter whether
you use an arrow function or normal function. But when you use a
framework that depends on this
, you should not use arrow functions.
For example, suppose you declare a Vue method using an arrow function. You won't be able to access the Vue instance's name
property because Vue won't be able to set this
.
const Vue = require('vue');
const app = new Vue({
data: () => ({ name: '' }),
// This method will **not** work. Vue methods depend on
// the correct value of `this`
methods: {
setName: newName => this.name = newName
},
template: `
<div>
<h1>{{name}}</h1>
<button v-on:click="setName('Hello')"></button>
</div>
`
});
Another common case is Mocha timeouts. You can use arrow functions for Mocha tests, but then you can't set the test timeout.
describe('MyFunction', () => {
it('works', () => {
this.timeout(500); // Throws an error
});
});
In general, you should not pass arrow functions to a framework
unless you do not intend to use the this
keyword. For example,
don't use arrow functions for Vue methods, Mocha tests, React class
methods, or Mongoose model methods. You may use arrow
functions inside a Vue method or a Mocha test, but the top level
function that you give to Vue or Mocha should not be an arrow function.