1

The function doSomethingElse in this example fails to execute since its this has been rebound to window or global (if in Node) due to a contextless call inside app.populateDatabase.

Is there any way to avoid this without referencing app inside every function?

loadDatabase function executes a callback according to a logic statement, if an imaginary database didn't exist, it populates it after loading, then the populateDatabase executes the callback it has been provided.

I cannot rebind the onLoaded argument to app since I don't know where it comes from, and the bind/apply/call abstraction overuse creates quite a mess.

var app = {};
app.loadDatabase = function(onLoaded) {

    // If database already exists, only run a callback
    var callback = onLoaded;

    // If database doesn't exists, populate it, then run a callback.
    if (!databaseExists) {
        callback = this.populateDatabase.bind(this, onLoaded);
    }

    this.database = new sqlite.Database("file.db", function(error) {
        if (error) { ... }

        callback();
    })

}

app.populateDatabase = function(onPopulated) {

    // Contextless call here. <--------
    onPopulated();
}

app.doSomethingElse = function() {

    // this != app due to contextless call.
    this.somethingElse();
}

app.run = function() {

    // Load the database, then do something else.
    this.loadDatabase(this.doSomethingElse);
}

app.run();
user7401478
  • 1,372
  • 1
  • 8
  • 25

1 Answers1

3

Just replace this.loadDatabase(this.doSomethingElse); with this.loadDatabase(() => this.doSomethingElse());. This way you create a new arrow function but then doSomethingElse is called with the right this context.

You could also do .bind but I recommend the arrow function. Here with bind: this.loadDatabase(this.doSomethingElse.bind(this))


In general consider to move to promises & maybe async functions. Then do this:

this.loadDatabase().then(() => this.doSomethingElse());

or better with an async function:

await this.loadDatabase();
this.doSomethingElse();
Lux
  • 17,835
  • 5
  • 43
  • 73
  • 1
    In that case, the arrow function should return the function's value instead of the function itself. `this.loadDatabase(() => this.doSomethingElse)`: passed parameter is an arrow function that returns a reference to `doSomethingElse`. `this.loadDatabase(() => this.doSomethingElse())`: passed parameter is an arrow function that calls `doSomethingElse` and returns the returned value. – user7401478 Sep 07 '18 at 23:56
  • yep. sorry, I corrected it. Feel free to edit in such cases ;) – Lux Sep 08 '18 at 00:08