0

I'm writing a simple word game to practice my javascript (I'm new to it) using the NPM package prompt (https://www.npmjs.com/package/prompt) to query the user when I need a response.

Since I come from an OOP background (in other languages) I've been experimenting with encapsulating different functionalities in different objects. So I have all the prompt related code in one object, like this

function Prompter() {

    this.getUserName = function (callback) {

        var schema = {
            properties: {
                name: {
                    description: "Tu nombre por favor:",
                    pattern: /^[ñÑa-zA-Z\s\-]+$/,
                    message: 'Solo letras, por favor',
                    required: true
                }
            }
        };
        prompt.get(schema, callback);
    };
}

and game logic in another object like this (this is the relevant part of the code)

function Game() {

    this.qGenerator = null;
    this.prompter = null;
    this.user = "";

    this.doNextRound = function () {
       //// omitted for brevity
    };

    this.init = function () {
        this.qGenerator = new QuestionGenerator();
        this.prompter = new Prompter();
    };

    this.startGame = function () {
        this.prompter.getUserName(this.storeUserName);
    };

    this.storeUserName = function (err, result) {
        if (err) {
            this.handleErr(err);
            return;
        }
        this.user = result.name;
        this.doNextRound();
    };
}

and I start the game like this

const game = new Game();
game.init();
game.startGame();

The problem I have is that in the Game method storeUserName, which I've passed as a callback to prompt, I have no access to the Game object through this, and thus, when I call

this.doNextRound

inside of storeUserNameI get

TypeError: this.doNextRound is not a function

I understand why, as this refers to Node inside the callback. But I don't know how to keep a reference to the correct this inside the method I'm passing as callback. I understand how to do it in more 'vanilla' Javascript -- using that = this, or apply,etc, but I'm not sure what the best way to handle this inside Node callbacks is when you're passing another object's methods. Any advice much appreciated.

Cerulean
  • 543
  • 2
  • 7
  • 17
  • Whoever flagged this for reopen should explain *why* it should be reopened. Voting to keep closed. – Mike Jun 10 '18 at 03:48

1 Answers1

1

Use Function.prototype.bind:

this.prompter.getUserName(this.storeUserName.bind(this));

or an arrow function:

this.prompter.getUserName( _ => this.storeUserName() );

Either of those will work.

Paul
  • 139,544
  • 27
  • 275
  • 264
  • Thanks! The first one is obvious once I see it. If you have a moment, could you explain the second answer? I understand that it's an arrow function with an unused input argument `_`, but the second part confuses me a bit. Without brackets, this represents the return value of the arrow function. So the arrow function is returning `this.storeUserName()`, that is the the arrow function is returning the result of calling `this.storeUserName`? And this is what's passed as a callback? Forgive my ignorance.... – Cerulean Jun 09 '18 at 16:22
  • 1
    @Cerulean The arrow function ***is*** the callback, it's not being called on that line. That would look something like: `(_ => this.storeUserName())()`. So when callback is called later, the arrow function gets called, which then calls `this.storeUserName()` with the correct `this` context. – Paul Jun 09 '18 at 17:09
  • 1
    @Cerulean It's similar to `this.prompter.getUserName( function ( ) { return this.storeUserName() } );` but that would not work because regular functions get their own `this` each time they're called, depending on how they're called. The arrow function doesn't have it's own `this`, so `this` inside it is the same as what it was when the function was defined. To get a regular anonymous function to behave like an arrow function you need to `.bind(this)` which you could do if you wanted to support an ES5 only JavaScript engine which was before arrow functions were added to the language. – Paul Jun 09 '18 at 17:15
  • 1
    This link describes what I'm talking about: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this – Paul Jun 09 '18 at 17:17
  • Thanks -- I follow that. I'm used to callbacks -- I used them for years in another language. I always think of it as the function to call _next_ -- But when I look at the following, even in the ES5 form, `this.prompter.getUserName( function ( ) { return this.storeUserName() } )` I'm thrown off a bit. I suppose what _this form_ expresses is not only which function to call next but that it is actually _calling_ that function. I'll look at the reference you provided. Thanks again! – Cerulean Jun 09 '18 at 19:30
  • `function ( ) { return this.storeUserName() }` is a function expression you could put other statements in it besides a return statement. In the above it's anonymous, but when it's passed in as the callback it's no different than a named function like: `function myFunc ( ) { return this.storeUserName() };` used as `this.prompter.getUserName( myFunc );` – Paul Jun 09 '18 at 20:12