3

I find this rather cumbersome. I probably am doing it wrong or there is some easier way I'm unaware of (did not find much w/o knowing what to search for).

I want to avoid doing the following two workarounds to ensure this is correctly pointing to the DB class inside my methods:

this.connect = this.connect.bind(this);
db.connect.bind(db)

DB class:

'use strict';

const http = require('http');
const MongoClient = require('mongodb').MongoClient;

class DB {
  constructor(opts){
    this.opts = opts;
    this.dbUrl = process.env.MONGODB_URL;

    // anyway to avoid this hacK?
    this.connect = this.connect.bind(this);
    this.close = this.close.bind(this)
  }

  async connect(ctx, next){
    try {
      ctx.db = await MongoClient.connect(this.dbUrl); // fixes `this` being incorrect here
    } catch(err) {
      ctx.status = 500;
      ctx.body = err.message || http.STATUS_CODES[ctx.status];
    }

    await next();
  }

  async close(ctx, next){
    const result = await ctx.db.close();
    await next();
  }
}

module.exports = DB;

Calling the DB class method db.connect():

'use strict';

const Koa = require('koa');
const DB = require('./db');
const db = new DB();
const bodyParser = require('koa-bodyparser');
const router = require('koa-router');

const api = router();
const app = new Koa();
const cors = require('kcors');

app
  .use(bodyParser())
  .use(cors())
  .use(db.connect); // the above constructor copying fixes having to do `db.connect.bind(db)` here

  // without the constructor fix in class DB:

  .use(db.connect.bind(db)) // this gets annoying having to do this everywhere

Which is more "correct" or if there is a 3rd way which avoids both of these workarounds? Also what are the pros and cons of each if these are the only two ways to bind class methods to their class.

chovy
  • 72,281
  • 52
  • 227
  • 295
  • One less hackish way is in an enclosure, i.e. `app.use((...args) => db.connect(...args))` – Yotam Ofek Dec 27 '16 at 03:59
  • That seems very verbose just to get `this` correct inside `async connect(){}` method...and if I understand correctly that would still not change it to `DB` – chovy Dec 27 '16 at 04:01
  • 1
    Serious question: Is using `bind()` actually considered a hack? I've always thought that it's a great way to bind the context when you lose it. I know alternatives are to use fat arrow functions (`() => {...}`) or assign the context to a variable (eg, `var context` or `var _this` or `var that` or `var self`) that you access via closure... but for me, `bind()` was often the most concise & reason-able way to do it. – therobinkim Dec 27 '16 at 04:03
  • That is the 'correct' way from what i've been told (don't know if this specific case it applies as "the best/easiest way"), but it is very cumbersome when every time you call a method on your class instance in your codebase you have to bind it to itself. – chovy Dec 27 '16 at 04:04
  • @chovy I think you might find http://stackoverflow.com/a/32192892/3814251 interesting as a potential solution – therobinkim Dec 27 '16 at 04:19
  • Thanks. It seems unless you use babel then doing the bind in the class constructor is the way to go. I'm unclear if each method is then copied on the instance though. – chovy Dec 27 '16 at 04:48
  • *"I'm unclear if each method is then copied on the instance though."* Yes, every time you call `.bind` a new function is created. ES2015 doesn't provide any new features to make binding `this` easier (except arrow functions maybe, but they don't really "bind" `this`). – Felix Kling Dec 27 '16 at 05:49

1 Answers1

0

There is a series of helpful auto binding functions. I wrote one just today to improve on them. Take a look: https://www.npmjs.com/package/auto-bind-inheritance

npm install --save auto-bind-inheritance

const autoBind = require('auto-bind-inheritance');

Just put autoBind(this); in the child class constructor and all methods and method pointers will be bound to the object, just like you would expect in C++/Java. This basically calls bind for you.

So, if you have trouble using methods as callbacks that get bound to another 'this', this package will fix it.

cwingrav
  • 949
  • 1
  • 10
  • 10