Arrow Function: What the heck is THIS anyway?

May 18, 2019

How nice it is to see the evolution of the programming language you code every day. Learning from mistakes, searching for better implementation, creating new features is what makes the progress from version to version.

This is happening to JavaScript these years, when ECMAScript 6 brings the language to a new level of usability: arrow functions, classes and a lot more. And this is quite Awesome!

Arrow Functions are one of the most impactful changes in ES6/ES2015, and they are widely used nowadays. They slightly differ from regular functions. Find out how

So What Are JavaScript Arrow Functions Anyway?

An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords. Arrow function expressions are ill suited as methods, and they cannot be used as constructors.
--MDN web docs

So Arrow functions are a more concise syntax for writing function expressions. They utilising a new token, =>, that looks like a fat arrow. They are anonymous and change the way this binds in functions. They make our code more prettier and concise, and simplify function scoping and the this keyword. They are roughly the equivalent of lambda functions in python or blocks in Ruby.

Let's look at the syntax:

There are couple of ways to define arrow functions( see MDN ) and I will try to provide a mere overview of each of them.

With Multiple Parameters

// ES5
var multiply_1 = function(a, b) {
  return a * b;
};

// ES6
const multiply_2 = (a, b) => { return a * b };

With One Parameter

//ES5
function multiply_1(params) {
  return params * 2
}
multiply_1(2);  // 4

//ES6
var multiply_2 = params => params * 2
multiply_2(2);  // 4

With No Parameters

//ES5
var getDoc_1 = function getDoc() {
    console.log(document);
};

//ES6
var getDoc_2 = () => { console.log(document); };

Object Literal Syntax

//ES5
var updateNameAndDept_1 = function setNameDept(name, dept) {
  return {
    name: name,
    dept:dept
  };
};

// ES6
var updateNameAndDept_2 = (name, dept) => ({ name: name, dept:dept });

Why use Arrow Functions?

There are two benefits to arrow functions.

First, they are less verbose than traditional function expressions:

const arr = [1, 2, 3];
const squares = arr.map(x => x * x);

// Traditional function expression:
const squares = arr.map(function (x) { return x * x });

Second, their this is picked up from surroundings (lexical). Therefore, you don’t need bind() or that = this, anymore. see this

function myComponent() {
    const button = document.getElementById('myButton');
    button.addEventListener('click', () => {
        console.log('I AM CLICK');
        this.handleClick(); // lexical `this`
    });
}

When use Arrow Functions?

When I first came across ES6, I found arrow functions really coooool but I was little confused until I got this wonderful explanation from Exploring ES6 (A book by Dr. Axel Rauschmayer).

While getting a better understanding of arrow functions I somewhere felt an urge to share the experience with those who face the same challenge. Sooo Guys, this is how it goes ==>

In Javascript, functions always have their own this but that also blocking us to access the this of surrounding method from inside a callback.

lets see this..

// ==> ES5 <== //

var greeting = "Hello";

function MyFunc(greeting) {
    this.greeting = greeting;
}
MyFunc.prototype.myTeamArr = function (teamArr) { // -->(3)
    return teamArr.forEach(function (player) { // -->(2)
       // Doesn’t work: give undefined
       console.log(this.greeting + player);  // -->(1)
    });
};
var welcomeTeam = new MyFunc('Welcome ');
welcomeTeam.myTeamArr(['-Hemant', '-Priyanka','-Akansha']);

// expected o/p: Welcome-Hemant Welcome-Priyanka Welcome-Akansha          
// actual o/p: Hello-Hemant Hello-Priyanka Hello-Akansha

Here our actual o/p is different from what we have expected. this is because the value of this in function (2) and function (3) are same and they are pointing to global space. that's why we are getting "Hello" instead of "Welcome". see the demo.

See the demo.

Well In ES5 we can deal with this problem in 3 ways.

Solution 1: that = this

We can assign this to a variable that isn’t shadowed. That’s what’s done in line (1), below:

var greeting = "Hello";

function MyFunc(greeting) {
    this.greeting = greeting;
}
MyFunc.prototype.myTeamArr = function (teamArr) {
    var that = this; // -->(1)
    return teamArr.forEach(function (player) {
       console.log(that.greeting + player);
    });
};
var welcomeTeam = new MyFunc('Welcome ');
welcomeTeam.myTeamArr(['-Hemant', '-Priyanka','-Akansha']);

// expected o/p: Welcome-Hemant Welcome-Priyanka Welcome-Akansha          
// actual o/p: Welcome-Hemant Welcome-Priyanka Welcome-Akansha

See the o/p.

Solution 2: specifying a value for this

See the last parameter in line (1).

var greeting = "Hello";

function MyFunc(greeting) {
    this.greeting = greeting;
}
MyFunc.prototype.myTeamArr = function (teamArr) {
    return teamArr.forEach(function (player) {
       console.log(this.greeting + player);
    },this);
};
var welcomeTeam = new MyFunc('Welcome ');
welcomeTeam.myTeamArr(['-Hemant', '-Priyanka','-Akansha']);

// expected o/p: Welcome-Hemant Welcome-Priyanka Welcome-Akansha          
// actual o/p: Welcome-Hemant Welcome-Priyanka Welcome-Akansha          

See the o/p.

Solution 3: bind(this)

We can use the bind() method.

function MyFunc(greeting) {
    this.greeting = greeting;
}
MyFunc.prototype.myTeamArr = function (teamArr) {
    return teamArr.forEach(function (player) {
       console.log(this.greeting + player);
    }.bind(this)); // see this
};
var welcomeTeam = new MyFunc('Welcome ');
welcomeTeam.myTeamArr(['-Hemant', '-Priyanka','-Akansha']);

// expected o/p: Welcome-Hemant Welcome-Priyanka Welcome-Akansha          
// actual o/p: Welcome-Hemant Welcome-Priyanka Welcome-Akansha          

See the o/p.

Now see the es6 solution.

ES6 solution: arrow functions

They are shorter in syntax and their this is picked up from surroundings (lexical).

Now see above code in ES6.

// ==> ES6 <== //

function MyFunc(greeting) {
    this.greeting = greeting;
}
MyFunc.prototype.myTeamArr = function (teamArr) {
    return teamArr.forEach((player) => {
       console.log(this.greeting + player);
    });
};
var welcomeTeam = new MyFunc('Welcome ');
welcomeTeam.myTeamArr(['-Hemant', '-Priyanka','-Akansha']);

See the o/p.

Apart from this, An extremely common example of arrow functions is to pull out a particular value of an object. see the code.

const names = objects.map(object => object.name);

Also,

const words = ['India', 'RUSSia', 'USA','chiNA'];
const downcasedWords = words.map(word => word.toLowerCase());

Another place where arrow functions can make code cleaner and more intuitive is in -Promises and Promise Chains. see example.

this.doSomethingAsync().then((result) => {
  this.storeResult(result);
});

So remember Arrow functions best used with anything that requires this to be bound to the context, and not the function itself.

When avoid Arrow Functions?

Arrow functions are neat but are not suitable for all use cases. here we will see some of the cases where we should avoid fat arrow =>.

With Object Methods

const player = {
    points: 50,
    score: () => {
        this.points++;
    }
}

Here we have our method called score, and whenever we call player.score, it should add one to our points, which is currently 50.

Now If we run player.score(); a few times, lets say 10 times. then points should be 60.But if I call player, points is still at 50. this is because of arrow functions and this.

2. With Object prototype

function Welcome(greeting) {  
  this.greeting = greeting;
}
Welcome.prototype.welcomeTeam = () => {  
  console.log(this === window); // => true
  return this.greeting;
};
var wel = new Welcome('Hello');  
wel.welcomeTeam(); // => undefined  

Try to do the same with normal function. and you will get the o/p.

3. In Callback functions with dynamic context

If you use arrow functions in these locations, that dynamic binding will not work, and you may get very confused as to why things aren't working as expected.

var button = document.getElementById('myButton');  
button.addEventListener('click', () => {  
  console.log(this === window); // => true
  this.innerHTML = 'i got clicked'; // will not work
});

this.innerHTML is equivalent to window.innerHTML and has no sense. use normal function here and see the magic.

4.  When it makes your code less readable

5. While Invoking constructors

var Message = (text) => {  
  this.text = text;
};
// Throws "TypeError: Message is not a constructor"
var myMessage = new Message('Hiii Guys!'); 

Still Confused???

See this wonderful chart by You Don't Know JS.

chart from https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch2.md

Conclusion

So I think Arrow functions are a phenomenal addition to the JS, and they are definitely useful.

arrowFunctions => “much cleaner and concise code”

Without doubt the arrow function is a great addition. However, like every other feature, they have pros and cons. We should use them as another tool in our toolchest.

Before you go…

Thanks for reading!

If you enjoyed this article, please hold down the clap button below 👏 to help others find it. The longer you hold it, the more claps you give!

And do not hesitate to share your thoughts in the comments below.

Hope this was helpful.

Like this article? Follow @hemantjava on Twitter

Thanks to @panzerdp and @kyleapennell.


Hemant Kumar Singh

Passionate about technology, design, startups and personal development.

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.