Traditional versus Arrow functions in JavaScript Classes

What’s up? Oh nothing, just doing `this` and `that`.

Suhan 🎃 Wijaya
JavaScript in Plain English

--

Source: Programmer Humor

This is a spiritual sequel to this article.

Create a class with a method using Traditional function like so. Let’s call this Approach A.

// APPROACH Aclass SomeClass {
constructor() {
this.someProp = 'someValue';
}
someMethod() { // Traditional function
console.log(this.someProp);
}
}

Create an instance of that class. When invoking the method on the instance, this refers to the instance. So far, it’s behaving as expected.

let instance = new SomeClass();instance.someMethod(); // logs 'someValue'

But, as soon as we assign the method to a variable and call that function variable, the method loses its context, and you get Uncaught TypeError: Cannot read property ‘someProp’ of undefined.

let instance = new SomeClass();let funcVariable = instance.someMethod;
funcVariable(); // logs error

OK.

Now, let’s create the class with a method using Arrow function like so. Let’s call this Approach B.

// APPROACH Bclass SomeClass {
constructor() {
this.someProp = 'someValue';
}
someMethod = () => { // Arrow function
console.log(this.someProp);
}
}

This now works.

let instance = new SomeClass();
let funcVariable = instance.someMethod;
funcVariable(); // logs 'someValue'

const { someMethod } = instance; // destructuring also works!
someMethod(); // logs 'someValue'

Why, JavaScript, why?

Source: Programmer Humor

As per MDN docs, “the class keyword is introduced in ES2015, but is syntactical sugar, JavaScript remains prototype-based.” So if we were to write in pre-ES6 syntax, Approach A looks like this.

// Equivalent to APPROACH A'use strict';var SomeClass = function() {
this.someProp = 'someValue';
}
SomeClass.prototype.someMethod = function() {
console.log(this.someProp);
}
var instance = new SomeClass();

The property someMethod is defined on the constructor function’s prototype.

Chrome Dev Console

But not on the instance.

Chrome Dev Console

You can access instance.someMethod through prototypal inheritance.

But when you assign instance.someMethod to another variable, the function variable loses its context.

Further, since “code within the class body's syntactic boundary is always executed in strict mode”, this will be undefined instead of defaulting to window or global.

OK.

Now, Approach B looks like this in pre-ES6:

// Equivalent to APPROACH B'use strict';var SomeClass = function() {
this.someProp = 'someValue';
var _that = this; this.someMethod = function() {
console.log(_that.someProp);
}
}
var instance = new SomeClass();

The property someMethod is not defined on the constructor function’s prototype.

Chrome Dev Console

Instead, it is defined on the instance.

Chrome Dev Console

Further, an Arrow function is bound to its surrounding lexical context by default (where it physically sits in the code), which seems equivalent to a Traditional function having access to an outer function variable that points to this (i.e., closure).

Hence, even when you assign instance.someMethod to another variable, the function variable remains bound to the instance context.

Note: I’m not 100% sure about the actual ‘under the hood’ mechanism by which Arrow functions derive this, so feel free to comment if you do know.

In any case, I went down this rabbit hole because I’ve been using Arrow functions for writing methods in classical React components, instead of binding Traditional functions (i.e., this.someMethod.bind(this)) in the constructor or when passing it down as prop.

import React from 'react';class SomeComponent extends React.Component {
constructor(props) {
super(props);
this.state = { /* some state */ };
}
someMethod = () => { // Arrow function
// will have access to `this.state`
}
render() {
// may attach `this.someMethod` as an event handler or
// pass it down as a prop to child components
}
}

Not endorsing one approach or the other, just describing the difference. Oh, and guess which browser is completely irrelevant to this whole discussion.

đŸ“« Hit me up on LinkedIn or email!

--

--