1

I found and unexpected behavior. Can anyone explain why "this" is lost when a method is referenced to a variable like in an example below?

class Foo {
  static bar() {
   return 'bar'; 
  }
  
  baz() {
   return this.constructor.bar(); 
  }
}

const foo = new Foo();

foo.baz(); // works fine

const baz = foo.baz;

baz(); // TypeError: Cannot read property 'constructor' of undefined

Gist: https://gist.github.com/sznowicki/c076c52a0242c77a3044f4a79e9af4c3

sznowicki
  • 1,241
  • 5
  • 14
  • 33

2 Answers2

1

Instead of:

const baz = foo.baz;

You can do:

const baz = foo.baz.bind(foo);

This will make sure that foo is bound to this in the method call when you run:

baz();

See the docs for more info:

Felix Kling
  • 705,106
  • 160
  • 1,004
  • 1,072
rsp
  • 91,898
  • 19
  • 176
  • 156
  • .apply also runs the method. The use case here is that I want to expose some methods to a global object which works as an API for some scripts. And I'm curious why exactly it happens. – sznowicki May 16 '17 at 12:57
  • just for a comment `` const baz = foo.baz.bind(foo); `` does the job – sznowicki May 16 '17 at 13:07
  • @sznowicki You can use `.bind` instead of `.apply` to set `this` without invoking the function. However, this basically just wraps your original function in a new function that stores a private reference to `this` and then runs `.apply` on the original function when invoked. – Lennholm May 16 '17 at 13:07
  • That is the solution for a problem. For some reason, I didn't think about that. – sznowicki May 16 '17 at 20:52
0

Think of this as an extra argument being passed to methods that carries an object on which method was invoked.
So: foo.baz() is invoked on foo and foo is passed as this.
But baz() is just a function (not method as it doesn't remember object on which it was defined previously). Here it is hard to tell what value this will hold (also depends on 'use strict';).

How to Handle It

  1. Bind it: const foo = bar.bind(instance).
  2. Call it with bar.apply(instance, ...) or bar.call(instance, ...).
  3. Wrap instance in function: const foo = function() { instance.bar() } or const foo = () => instance.bar().
  4. Remember, that arrow functions handle this differently from plain functions.
ilyaigpetrov
  • 3,149
  • 3
  • 25
  • 42