Introduction to arrow functions in JavaScript ES6

Original author: Alex Gorbatchev
  • Transfer
“Thick” arrow functions (=>), also known as arrow functions, are completely new functionality in ECMAScript 2015 (previously known as ES6). If you believe the rumors, then in ECMAScript 2015 => syntax was used instead of -> syntax under the influence of CoffeeScript . Also, the similarity of this context transfer played an important role.

Arrow functions have two main tasks: to provide a more concise syntax; provide lexical this with parent scope. Let's take a closer look at each of them!

New function syntax

The classic syntax of functions in JavaScript is stiff, whether it's a function with one variable or a page with many functions. Each time you declare a function, you need to write function () {}. The need for a more concise syntax of functions was one of the reasons why CoffeeScript became very popular at one time . This need is especially evident in the case of small callback functions. Let's just take a look at the Promise chain:
function getVerifiedToken(selector) {
  return getUsers(selector)
    .then(function (users) { return users[0]; })
    .then(verifyUser)
    .then(function (user, verifiedToken) { return verifiedToken; })
    .catch(function (err) { log(err.stack); });
}

At the top, you see more or less digestible code written using the classic function syntax in JavaScript. And here is the exact same code rewritten using arrow syntax:
function getVerifiedToken(selector) {
  return getUsers(selector)
    .then(users => users[0])
    .then(verifyUser)
    .then((user, verifiedToken) => verifiedToken)
    .catch(err => log(err.stack));
}

Here you need to pay attention to several important points:
  • We have lost function and {} because our callback functions are written on one line.
  • We removed (). Now they do not wrap the argument list when only one argument is present (the remaining arguments pass as exceptions; for example, (... args) => ...).
  • We got rid of the return keyword. By removing {}, we allow one-line arrow functions to implicitly return (in other languages, such functions are often called lambda functions).

Once again, pay attention to the last point. Implicit returns only occur with single-line arrow functions. When an arrow function is defined with {}, even if it is a separate operator, an implicit return does not occur.
const getVerifiedToken = selector => {
  return getUsers()
    .then(users => users[0])
    .then(verifyUser)
    .then((user, verifiedToken) => verifiedToken)
    .catch(err => log(err.stack));
}

Here the fun begins. Since our function has only one operator, we can remove {}, and the code will be very similar to CoffeeScript syntax :
const getVerifiedToken = selector =>
  getUsers()
    .then(users => users[0])
    .then(verifyUser)
    .then((user, verifiedToken) => verifiedToken)
    .catch(err => log(err.stack));

Still, the code above is written using ES2015 syntax. (I was also surprised that it compiled perfectly .) When we talk about arrow functions with one operator, this does not mean that the operator cannot occupy more than one line, for ease of use.

However, there is one significant minus: removing {} from the arrow functions, how can we return an empty object? For example, the same {}?
const emptyObject = () => {};
emptyObject(); // ?

And here is the whole code together:
function () { return 1; }
() => { return 1; }
() => 1
function (a) { return a * 2; }
(a) => { return a * 2; }
(a) => a * 2
a => a * 2
function (a, b) { return a * b; }
(a, b) => { return a * b; }
(a, b) => a * b
function () { return arguments[0]; }
(...args) => args[0]
() => {} // undefined
() => ({}) // {}

Lexical this

The story of how they tried to drag this into JavaScript was already dusty. Each function in JavaScript sets its own context for this. This context, on the one hand, is very easy to get around, and, on the other hand, it is extremely annoying. In the example below, you see the code for a watch that updates data every second, accessing jQuery:
$('.current-time').each(function () {
  setInterval(function () {
    $(this).text(Date.now());
  }, 1000);
});

When we try to refer to this DOM of the element specified through each in the setInterval callback, we, unfortunately, get a completely different this, the one that belongs to callback. You can get around this point by setting the that or self variable:
$('.current-time').each(function () {
  var self = this;
  setInterval(function () {
    $(self).text(Date.now());
  }, 1000);
});

Thick arrow functions can help solve this problem, since they do not have this:
$('.current-time').each(function () {
  setInterval(() => $(this).text(Date.now()), 1000);
});

What about the arguments?

One of the downsides of arrow functions is that they do not have their own arguments variable, like ordinary functions:
function log(msg) {
  const print = () => console.log(arguments[0]);
  print(`LOG: ${msg}`);
}
log('hello'); // hello

Again, arrow functions do not have this and arguments. However, taking this into account, you can still get the arguments passed to the arrow functions using rest parameters (also known as spread operators):
function log(msg) {
  const print = (...args) => console.log(args[0]);
  print(`LOG: ${msg}`);
}
log('hello'); // LOG: hello

What about generators?

Thick arrow functions cannot be used as generators. There are no exceptions and workarounds. Point.

Conclusion

“Thick” arrow functions are one of the reasons I love JavaScript so much. It's very tempting to just start using => instead of function. I have seen whole libraries where only option => is used. I do not think, however, that this is reasonable. After all, => has many features and hidden functions. I recommend using arrow functions only where you need new functionality:
  • Functions with single operators that immediately return;
  • functions that should work with this with the parent scope.

ES6 today

So is it possible to take advantage of ES6 today? Using transpilers has become the norm in the last few years. Neither simple developers nor large companies are embarrassed to use them. Babel is a transporter from ES6 to ES5 that supports all ES6 innovations.

If you use Browserify , you can add Babel in just a couple of minutes . Of course, there is support for almost any build with Node.js. For example: Gulp, Grunt and many others.

What about browsers?

Most browsers are gradually adding new features , but no one has full support yet. What, wait now? It depends. It makes sense to start using language features that will become universal in a year or two. This will allow you to comfortably move to a new stage. However, if you need 100% control over the source code, then it’s better to wait and use ES5.

Translation prepared by: greebn9k (Sergey Gribnyak), silmarilion (Andrey Khakharev)

Also popular now: