An Introduction to Arrow Functions and Lexical ‘this’

11 minute read

Introduction

Arrow functions are one of the most popular features that come with ES6(ECMAScript 2015). They have proved to be a fine alternative to regular function expressions.

In this tutorial, we will learn how to write arrow functions, how this works in arrow functions, and finally, when to use or avoid arrow functions.

What are Arrow Functions?

Arrow functions - also known as “Fat Arrows” are anonymous functions with a concise syntax.

Let’s take a look at a regular function:


const sayName = function(name) {
 console.log("Your name is " + name);
}

Now, compare that with an arrow function:

const sayName = (name) => console.log("Your name is" + name);

As you can see, an arrow function is more concise and it allows us to write readable one-liners.

They also come with a lot of benefits that we are going to look into in great detail such as:

  • Implicit return - they can return values without using the return keyword.
  • They do not rebind the value of this.

In the coming sections, we will learn how to write arrow functions. You can try the code samples in the browser console.

To open the console. Open a new empty tab and then press CTRL+Shift+J on Windows or CTRL+Option+J on Mac.

info: I have chosen to use var instead of const/let to prevent “identifier has already been declared” errors when you paste the code samples in the console.

Arrow Function Syntax with no Parameters

Let’s begin by learning how to write an arrow function that takes no parameters.

We will use an example of a regular function that returns the sum of two numbers.

Regular function:

var add = function() {
 return 2 + 3;
}

add() // 5 

To turn it into an arrow function, remove the function keyword and add a fat arrow => next to the empty parentheses:

var add = () => {
 return 2 + 3;
}

add() // 5

Implicit Return

An implicit return is when a value is being returned without using the return statement.

An implicit return works only when you have one statement inside the curly braces.

Our function has only one statement which makes it a good candidate for an implicit return.

To do an implicit return, removing the curly braces and the return keyword:

var add = () => 2 + 3;

add(); // 5

Look at how concise the syntax is!

Arrow Function with One Parameter

Let’s change our previous example(regular function), and pass it one parameter.

Regular function:

var add = function(x) {
 return 2 + x;
}

add(3) // 5

Let’s first turn it into an arrow function with an implicit return like we did before. The only difference is that instead of having empty parentheses, we will pass one parameter:

var add = (x) => 2 + x;

add(3) // 5

When we have only one parameter, we can also omit the parentheses:

var add = x => 2 + x;

add(3) // 6

Arrow Functions with Multiple Parameters

Lets now give our regular function two parameters.

Regular function:

var add = function(x, y) {
 return x + y;
}

add(2, 3) // 5

Arrow function:

var add = (x, y) => x + y;

add(2,3) // 5 

When a function has multiple parameters, you must put the parentheses. You can remove them only when you have one parameter.

Object literal

Arrow functions can also return object literals. You need to wrap them inside the parentheses to prevent them from being treated as block code.

Regular function:

var person = function() {
 return {
 age: 23,
 name: 'stanley'
 }
} 

person() // {age: 23, name: "stanley"}

Arrow function:

var person = () => ({age: 23, name: 'stanley'});

person() // age: 23, name: "stanley"}

When you omit the parentheses, it will throw an error because the object will be treated as a block code.

var person = () => {age: 23, name: 'stanley'};

person() // Uncaught SyntaxError: Unexpected token :

Arrow functions and lexical this

Let’s now learn how this works in an arrow function.

this in arrow function works differently from this in a regular function.

If you do not understand how this works in a regular function, I would recommend you learn more about it before proceeding since it is a very important topic in JavaScript.

You can learn more about this from MDN: this - JavaScript

A quick refresher:

  • this outside any function points to the global object/window object. You can see that by typing this or this === window in the console.

  • Regular functions define their own this, and the value of `this’ always points to object calling the method.

Let me now explain how this works in arrow functions.

An arrow function does not define it’s own this, but it looks for this in the current scope and the enclosing scopes the same way that a variable is looked up when it being used inside a function.

Let’s recap on what happens when we define and use a variable inside a function:

var x = 4;

function showNumber() {
 var x = 5;
 console.log(x);
}

showNumber() // 5

When you call the function, it will show 5 as the output because it first checks for the existence of the x variable inside the function scope. If the variable definition is found inside the function scope, it is used. In our example, x is defined in the function scope, and it is the one that is used.

Now let us remove the x variable inside the function and see what happens:

var x = 4;

function showNumber() {
 console.log(x);
}

showNumber() // 4 

It shows 4 as the output. When the function is called, the variable x is checked if it has been defined in the function scope first. In our case, it hasn’t. So it will then look for the x variable definition in the enclosing scope which turns out to be the global scope. It will use the x variable defined in the global scope which encloses our function, hence why the function prints 4.

The biggest takeaway from this is how the variable x is looked up.

Now, let’s see how this works in arrow functions. As I said, the arrow functions do not define this, hence it is looked up in the current scope and the enclosing scopes until it is found. This happens the same way a variable is looked up.

We can understand this behavior clearly by comparing this in a regular function and this in an arrow function.

this in a Regular function:

var person = {
 name: 'stanley',
 sayName: function() {
 console.log(this.name)
 }
}

person.sayName() // stanley

It prints stanley because this points to the object calling the method ‘sayName() which is person`.

Compare this in an arrow function:

var person = {
 name: 'stanley',
 sayName: ()=> { 
 console.log(this.name)
 }
}

person.sayName() // 

If you run the code, you will see that no output is shown. Let’s check why by logging this into the console from the arrow function:

var person = {
 name: 'stanley',
 sayName: ()=> { 
 console.log(this)
 }
}

person.sayName() // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

This is pointing to the window object, “why could this be?”, you might ask.

The answer is when you create an object, this is automatically defined and it points to the window object. You do not have to take my word. Let’s see it ourselves:

var person = {
 whatIsThis: this 
}
person.whatIsThis; // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

As you can see this inside the object is pointing to the global object/window object.

So continuing with the previous example:

var person = {
 name: 'stanley',
 sayName: ()=> { 
 console.log(this)
 }
}
person.sayName()

When we called the method sayName(), it checks in the sayName() method scope if this has been defined but it doesn’t find it(remember arrow funtion do not define this).

It then looks in the enclosing scope which in our case is the person object. It finds this there which has been defined by the object and it points to the window object, and it uses it and stops looking.

That’s why we got the output showing this points to the window object.

Let’s look at a second example, you can use Codepen or your favorite text editor to follow along.

Create an index.html file and paste the following code:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>this - javascript</title>
</head>
<body>
 
 <a id="btn" href="#">Click Me</a>
 <script src="main.js"></script>
</body>
</html>

Now, let us set up an event listener with a regular function first.

Create ‘main.js’ and put the following code:

const btn = document.getElementById('btn');

btn.addEventListener('click', function() {
 console.log(this);
})

Open your index.html file in your browser and open the browser console and then click on the link:

// output
<a id="btn" href="#">Click Me</a>

When the link has been clicked, the regular function is called and this points to the element the event listener is attached to.

Info: Remember the value of this in a regular function depends on how the function is called.

Let’s now see what happens we pass an arrow function into the event listener:

btn.addEventListener('click', () => {
 console.log(this);
})

When you click on the link and check in the browser console, you will get the following output:


Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, }

We have the window object as the output because this is looked in the arrow function when the event is fired but it is not found. It then looks in the enclosing scope which happens to be the global scope where it will find this which points to the window object.

Let’s modify our previous regular function and add a setTimeout method inside.

A setTimeout() is a window method that calls a function once after a specified number of milliseconds.

btn.addEventListener('click', function() {
 setTimeout(function() {
 console.log(this);
 }, 1000);
});

Can you guess what this going to be?

Well, here is output:

Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, parent: Window, …}

Remember, setTimeout is a window method. So this points to the window object since the setTimeout method is called by the window.

The previous example could also be rewritten like this and it will show the same output:

btn.addEventListener('click', function() {
 window.setTimeout(function() {
 console.log(this);
 }, 1000);
});

Perhaps in this example, you can see why this is pointing to the window object. Notice how the setTimeout method is being called by the window object? Like I said before, setTimeout is a window object. So even when you omit window when calling the setTimeout method like in the previous example, it will still be called by the window object.

Lets now convert the setTimeout regular function into the an arrow function. We are going to leave the regular function in the event listener as is, so that this points to the element that was clicked:

btn.addEventListener('click', function() {
 setTimeOut(() => {
 console.log(this);
 }, 1000)
})

Can you guess the output of this in the setTimeout method based on what we have learned? Think about it for a moment.

If you guessed it correctly, the output will be the element that was clicked.

<a id="btn" href="#">Click Me</a>

When the setTimeout() method is called, the arrow function looks for this in its function scope but it doesn’t find. It then look for this in the enclosing scope(the event listener) where it finds it and uses it.

The value of this is the element that was clicked, we already looked into why this was the case in the previous examples.

I hope you have now understood how this works in the arrow functions. Next, we will look at when to use arrow functions.

When to use arrow functions

Arrow functions can be effectively used in callbacks(functions passed into other functions)

The following are some of the ways that arrow functions can come in handy.

Array methods

Arrow functions are frequently used with Array methods like filter, forEach, Map, etc. because of their conciseness.

Array.Map() with a regular function:

var numbers = [1, 2, 3, 4];

numbers.map(function(num){
return num * 2;
});

// [2, 4, 6, 8]

Array.Map() with a arrow function:

var numbers = [1, 2, 3, 4];

numbers.map(num => num * 2);

// [2, 4, 6, 8]

Promises and Fetch API

Fetch API which implements a promise takes callback functions which can greatly benefit from arrow function concise syntax.

Lets see the Fetch API with a regular function:

const url = 'url comes here';
fetch(url)
.then(function(res) {
 return res.json()
})
.then(function(data) {
 console.log(data)
})
.catch(function(e){
 console.log(error)
})

Now, let’s compare it with an arrow function:

const url = 'url comes here';
fetch(url)
.then(res =>res.json())
.then(data => console.log(data))
.catch(error => console.log(error));

With an arrow function, our code is much shorter and readable.

When to avoid arrow functions

When you need to use this in an event listener

We have already looked at what happens when you use an arrow function in an event listener, this points to the window object.

So in cases where you need to to use this in an event listener, it is best to use a regular function:

const btn = document.getElementById('btn');

btn.addEventListener('click', function() {
 this.innerHTML = 'you clicked me'
});

On a side note, you can still access the element that an event listener is attached to when you pass an event object as a parameter. You can get the target element using event.target:

const btn = document.getElementById('btn');

btn.addEventListener('click', (event) => {
 target = event.target;
 target.innerHTML = 'you clicked me'
});

When creating a method for an object

We also looked at what happens when you create a method with an arrow function, this points to window object instead of the object calling the method.

So it’s best to use a regular function when creating a method for an object.

const person = {
 name: 'stanley',
 sayName: function() 
 console.log(this.name)
}

person.sayName() // stanley

Conclusion

In this article, you have learned how to write arrow functions. I hope you now have a firm grasp on how this works in the arrow function and when to use or avoid arrow functions.

Thanks for reading this article, if you have any feedback feel free to leave a comment.

Comments