Assigning a static method as an expression to a variable results in "this" as undefined. Why does the this context get lost??

A class itself is a function:

class Foo {}
console.log(typeof Foo)
// function

Static methods are called on the class itself, instead of an instance of a class.

I declare a static method b, which returns another static method a from the same class. When I now assign the static method b as an expression to a variable like const testB = Meme.b, the this-context gets lost (undefined).

I do not understand why this happens. Can someone shine some light on why this is?

Here is a more elaborate example (codepen):
:white_check_mark: = expected output
:x: = unexpected output

class Meme {
  static a() {
    return 1;
  }

  static b() {
    return this.a()
  }
}

// ---

/*
*  works as expected
*/
console.log( Meme.a() ) // output: 1 ✅ 

const testA = Meme.a

testA() // output: 1 ✅ 


// ---

/*
* When binding Meme.b to a variable, the `this` context gets lost.
* Does _NOT_ work as expected
*/
console.log( Meme.b() ) // output: 1 ✅ 

const testB = Meme.b;

testB() // expected output: 1 ❌
// actual output: Uncaught TypeError: Cannot read property 'a' of undefined
// -- why is 'this' undefined? why does fn not bind to the Meme-context?

You're not using auto-binding functions (the arrow functions) - this means the functions do not store the "this" context at all.

The only reason Meme.b() works, is because Javascript automatically uses what's on the left-hand side of the "." as the "this" value whenever calling a function on the right-hand side. (Arrow functions will just ignore this provided "this" value and use the one they captured instead). As you can see, when you've plucked that function off and called it directly, you're not calling it from an object anymore (i.e. you're not using a "." when calling it), so Javascript has no idea what the "this" should be, so it sets it to undefined.

With these principles in mind, here's a fun code snippet:

class Meme {
  static a() {
    return 1;
  }

  static b() {
    return this.a()
  }
}

const anotherObj = {
  a() {
    return 5
  },
  b: Meme.b,
}

console.log(anotherObj.b()) // 5
1 Like

Another way to reason about this code is that it is effectively the same as this:

function _a() { return 1; }
function _b() { return this.a(); }

function Meme() {}
Meme.a = _a;
Meme.b = _b;
2 Likes

Thank you two.
Honestly, it felt quite counter-intuitive at first, and I totally forgot that I could just use an arrow function for that. Mea culpa :sweat_smile:

Just for completeness:

class Meme {
  static a() {
    return 1;
  }

  static b() {
    return this.a()
  }
}

// const testB = Meme.b  // <- don't use this
const testB = () => Meme.b // <- use this

testB() // output: 1 ✅ 

Works like charm now! Thanks again! :)

1 Like

You can use an arrow function in a static field too

class Meme {
  static a() {
    return 1;
  }

  static b = () => {
    return this.a()
  }
}

const testB = Meme.b

testB() // output: 1 ✅ 
1 Like