Complete Intro Modern Javascript
Arrow Functions

Arrow Functions and Closures

Arrow functions are probably the most used feature in Modern JavaScript.

But JavaScript has SO many ways to define functions. Why Do We Need Another Way?

// The arrow function syntax:
const doSomething = () => {
  // Function Scope
};

This new “shorter” syntax to define functions is popular NOT only because it’s shorter but also because it behaves more predictably with closures. This latter point is really what’s important so let’s learn what that’s all about.

Arrow functions give access to their defining environment while regular functions give access to their calling environment. This access is possible through the special this keyword:

  • The value of the this keyword inside a regular function depends on HOW the function was CALLED.

  • The value of the this keyword inside an arrow function depends on WHERE the function was DEFINED.

Here is a code example to expand on the explanation. Try to figure out what will be printed in Output #1 through #4 (last 4 lines):

this.whoIsThis = 'TOP'; // Identify this scope

// 1) Defining
const fancyObj {
  whoIsThis: 'FANCY', // Identify this object
  regularF: function () {
    console.log('regularF', this.whoIsThis);
  },
  arrowF: () => {
    console.log('arrowF', this.whoIsThis);
  },
};

// 2) Calling
console.log('TOP-LEVEL', this.whoIsThis); // It's "TOP" here

fancyObj.regularF(); // Output #1 (Fancy)
fancyObj.arrowF();   // Output #2 (Top)

fancyObj.regularF.call({whoIsThis: 'FAKE'}); // Output #3 (Fake)
fancyObj.arrowF.call({whoIsThis: 'FAKE'});   // Output #4 (Top)

You have a regular function (regularF) and an arrow function (arrowF) defined in the same environment, and called by the same caller. Here’s the explanation of the outputs in the last 4 lines:

  1. The regular function will always use its this to represent who called it. In the example above, the caller of both functions was the fancyObj itself. That’s why Output #1 was "FANCY".

  2. The arrow function will always print the this scope that was available at the time it was defined. That’s why Output #2 was "TOP".

  3. This holds true even if the caller was changed using .call, .apply, or .bind (which are functions that can be used to change the calling environment). The first argument to these functions becomes the new "caller". That’s why Output #3 was "FAKE"

  4. The arrow function does not care about this caller change. It’ll still output "TOP".

You can experiment with this example at jsdrops.com/arrow-functions.

One other cool thing about arrow functions is that if the function only has a single return line (like square1 below) you can make it even more concise by removing the curly brackets and the return keyword altogether (as in square2). You can also remove the parentheses around the argument if the function receives a single argument:

const square1 = (a) => {
  return a * a;
};
const square2 = (a) => a * a;
const square3 = a => a * a;

The last syntax is usually popular for functions that get passed to array methods like map, reduce, filter, and other functional programming methods:

console.log([1, 2, 3, 4].map(a => a * a));

Note that if you want to use the arrow-function one-liner version to make a function that returns an object, you’ll have to enclose the object in parenthesis because otherwise, the curly brackets you think are for object literals are actually the scope of the function.

// Wrong
const objMaker = () => { answer: 42 };

// Right
const objMaker = () => ({ answer: 42 };

The above is actually one of the most common mistakes beginners do when working with libraries like React.

Guess what, the confusion about curly brackets are NOT only about scopes and object literals. There are many more things you can do with curly brackes. Keep reading. 😎