Search code examples
javascriptnode.jsscoping

JavaScript Class Weird Scoping


Assuming I have two files. One file with a class where the method hello just console.logs this:

// Class.js
class Test {
  constructor() {
    this.inside = true;
  }

  hello() {
    console.log('inside hello')
    console.log(this);
  }
}

module.exports = new Test();

and another file which executes the method hello of this class:

// index.js
const Test = require('./Class.js');

Test.hello();
// -> 'inside hello'
// -> { inside: true } 

Everything works as expected, the this in the hello() method has the correct scope. But, when I make a new instance of the class and export just hello of this new instance:

// Class.js
class Test {
  constructor() {
    this.inside = true;
  }

  hello() {
    console.log('inside hello')
    console.log(this);
  }
}

module.exports = (new Test()).hello; // <- just hello is exported

then the scoping of the hello() changed and it seems it is not a part of the class anymore:

// index.js
const hello = require('./index.js');

hello();
// -> 'inside hello'
// -> undefined

Is there a reason, why this single exported function acts so differently?


I tried it in Python, and it worked (maybe it does in other languages as well):

# Class.py
class Class:
  def __init__(self):
    self.test = 'hello'

  def hello(self):
    print('inside hello')
    print(self)

hello = Class().hello


# index.py
from Class import hello

hello()
# -> 'inside hello'
# -> <Class.Class instance at 0x10e26e488>

Solution

  • When you call hello() standalone, it has no calling context - it's not being called from an object, when said object would ordinarily be its calling context. (such as Test.hello(); - the calling context would be Test in that line)

    If you want to bind its calling context so that it's usable as a standalone function, you should export the bound function, such as:

    const test = new Test();
    module.exports = test.hello.bind(test);