11 December 2023

The biggest reason I never use classes in JavaScript

JavaScript methods that use this in reference to the class they belong to will break if the method is assigned to another value or passed into another function as a function object.

This is because the value of this is dependent on the object that the method is a member of at the time of calling.

This means that the code calling a class's method can break it!

In this example, calling g, which refers to f, will cause an error.

class A {
    x;

    constructor(v) {
        this.x = v;
    }

    f() {
        return this.x;
    }
}

const a = new A(5);
a.f();
// 5

const g = a.f;

g();
// Uncaught TypeError: Cannot read properties of undefined (reading 'x')
//     at f (REPL11:9:21)

But with a slight change, using a function that returns an object to construct our object, instead of a class (crucially avoiding the usage of the this keyword), we avoid this problem and can pass our method around as a function object without issue.

function getA(v) {
    
    function f() {
        return v;
    }

    return {
        f
    }
}

const a2 = getA(5);
a2.f();
// 5

const g2 = a2.f;

g2();
// 5

It baffles me that, at some point, someone decided that this was how classes should behave.

Another interesting consequence of this is that we can take functions that refer to properties of their class, and graft them onto a different object.

For example:

const a3 = new A(5);
a3.f();
// 5

const b = { x: 10 };
b.h = a3.f;
b.h();
// 10

Again, this is worth avoiding, using this is asking for trouble.

Tags: JavaScript