8

I've just started to experiment with classes and async await. I'm using Node version 8.9.0 (LTS). When I console.log(this), I get undefined instead of the reference to the object.

subhandler.js

class Handler {
  constructor(props) {
    this.defaultRes = {
      data: successMessage,
      statusCode: 200
    };
  }

  async respond(handler, reply, response = this.defaultRes) {
    console.log(this); // why is `this` undefined????
    try {
      await handler;
      return reply(response.data).code(response.statusCode)
    } catch(error) {
      return reply(error);
    }
  }
}

class SubHandler extends Handler {
  constructor(props) {
    super(props);
    this.something = 'else';
  }

  makeRequest(request, reply) {
    console.log(this); // why is `this` undefined!!
    // in this case, doSomeAsyncRequest is a promise
    const handler = doSomeAsyncRequest.make(request.params);
    super.respond(handler, reply, response);
  }
}

module.exports = new SubHandler;

Inside Hapi route

const SubHandler = require('./subhandler');

server.route({
    method: 'GET',
    path: '/',
    handler: SubHandler.makeRequest,
    // handler: function (request, reply) {
    //  reply('Hello!'); //leaving here to show example
    //}
});

Prototype example

function Example() {
  this.a = 'a';
  this.b = 'b';
}

Example.prototype.fn = function() {
  console.log(this); // this works here
}

const ex = new Example();
ex.fn();
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
cusejuice
  • 10,285
  • 26
  • 90
  • 145
  • How are you calling `makeRequest`? – TimoStaudinger Nov 14 '17 at 16:40
  • It is called from a Hapi route handler https://hapijs.com/tutorials/routing – cusejuice Nov 14 '17 at 16:43
  • For this kind of issue, usually there's a `.bind(this)` missing in the call. – Felipe Freitag Vargas Nov 14 '17 at 16:44
  • Sounds like a basic JS `this` issue. Like [here](https://stackoverflow.com/questions/34930771/why-is-this-undefined-inside-class-method-when-using-promises) and [here](https://stackoverflow.com/questions/591269/settimeout-and-this-in-javascript) and [here](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) and [here](https://stackoverflow.com/questions/2025789/preserving-a-reference-to-this-in-javascript-prototype-functions) and many other questions probably. – noppa Nov 14 '17 at 16:50
  • `async/await` is part of ES2017, not ES7 (ES2016) – Felix Kling Nov 15 '17 at 05:39

1 Answers1

13

If you want this to always point to the instance in makeRequest, bind its context in the constructor:

class SubHandler extends Handler {
  constructor(props) {
    super(props);

    this.makeRequest = this.makeRequest.bind(this)

    this.something = 'else';
  }

  makeRequest(request, reply) {
    console.log(this);
    const handler = doSomeAsyncRequest.make(request.params);
    super.respond(handler, reply, response);
  }
}
TimoStaudinger
  • 41,396
  • 16
  • 88
  • 94
  • 1
    Hm, that works, but am I missing something here? I figured that `this` would work out-of-the-box using the new ES6 class keyword. So basically, for all the methods of the class to get a reference to `this` I'd have to bind its context in the constructor?? Seems odd – cusejuice Nov 14 '17 at 16:50
  • 2
    No, classes do not autobind their member functions in JavaScript. – TimoStaudinger Nov 14 '17 at 16:55
  • 2
    `class` is mostly just syntactical sugar over the prototype-based inheritance that JS has had for ages. `this` semantics and most other behaviour is still the same. – noppa Nov 14 '17 at 16:57