What is a good way to automatically bind JS class methods?

Viewed 17013

I get tired of writing code like this:

class Something {

    constructor() {

        this.method = this.method.bind(this);
        this.anotherOne = this.anotherOne.bind(this);
        // ...
    }
}

It's time consuming and it's easy to forget to bind a method. I aware of the class fields proposal, but it's still Stage 3 and seems to come with some issues.

My current solution (based on this answer) looks like this:

class Something {

    constructor() {

        // Get all defined class methods
        const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(this));

        // Bind all methods
        methods
            .filter(method => (method !== 'constructor'))
            .forEach((method) => { this[method] = this[method].bind(this); });
    }
}

This seems to work, but I'm wondering if there is a better way, or if this method has issues that I'm not aware of.

Update: Why Do This?

The problem I have run into is that if I don't bind my class functions in the constructor, I have to remember to call them "properly" later. For example:

const classInstance = new Something();

// This fails for a non-obvious reason
someAction()
    .then(classInstance.method);

// This works of course but looks like we created a useless function if you don't know better
someAction()
    .then(result => classInstance.method(result));
5 Answers

Use fat arrow function in ES6 (generally called as arrow function)

anotherOne = ()=> {
...
}

Call like this onClick={this.anotherOne}; no need to bind in constuctor

From the ECMA spec

Any reference to arguments, super, this, or new.target within an ArrowFunction must resolve to a binding in a lexically enclosing environment. Typically this will be the Function Environment of an immediately enclosing function.

You can also use auto-bind like this

From usage example:

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

class Unicorn {
    constructor(name) {
        this.name = name;
        autoBind(this);
    }

    message() {
        return `${this.name} is awesome!`;
    }
}

const unicorn = new Unicorn('Rainbow');

// Grab the method off the class instance
const message = unicorn.message;

// Still bound to the class instance
message();
//=> 'Rainbow is awesome!'

// Without `autoBind(this)`, the above would have resulted in
message();
//=> Error: Cannot read property 'name' of undefined

It seems a bit late to answer this question but there is no accepted answer so I will try my best for people come here after me.

To automatically bind this for all methods, you can use "arrow" function:

class Something {
  constructor() {
    // Don't need to bind `this`
  }

  doSomething = () => {
    console.log(this); // `this` will be pointed to the instance
  }
}

const s = new Something();
s.doSomething(); // => `this` is pointed to `s`

Note: just make sure the classes extending this class Something will not use doSomething method in its constructor (eg: super.doSomething()), otherwise you will get an error.

To answer the updated question: if you don't manually bind this using .call() or .apply(), the value of this depends on the way that method is called

For example:

class Something {
  constructor() {
    // didn't bind `this` here
  }

  doSomething() {
    console.log(this);
  }
}

const s = new Something();
const funcA = s.doSomething;
const object = {
  funcB: s.doSomething,
};

// these ways of calling `.doSomething()` result in different values of `this`:
funcA(); // => `this` is pointed to the global variable (`window` in browser environment)
window.funcA(); // => same as `funcA()`
s.doSomething(); // => `this` is pointed to `s`
object.funcB(); // =? `this` is pointed to `object`

Beside that, the implementation of .then() method would be similar like this:

class Promise {
  // ...
  then(callback) {
    // code...
    callback(); // notice how the `callback` is called - not as a method of an object
    // code...
  }
}

With your code example, the way you pass the callback into .then() method will affect the value of this inside the callback:

const classInstance = new Something();

// `this` inside `classInstance.method` is pointed to `this` inside the
// `.then` method, not the `classInstance`, as `classInstance.method()`
// will be called as `callback()`
someAction()
    .then(classInstance.method);

// `this` inside `classInstance.method` is pointed to `classInstance`,
// as the way the anonymous "arrow" function is called does not affect the way
// `classInstance.method` is called, so `this`'s value is controlled by the way
// you call it inside the callback (anonymous "arrow" function) of `.then()`
// method.
someAction()
    .then(result => classInstance.method(result)); 

You can use an ES6 arrow function:

method = () => {
    //Do stuff
}

As stated in the docs:

An arrow function expression is a syntactically compact alternative to a regular function expression, although without its own bindings to the this, arguments, super, or new.target keywords.

Class fields can be used to create functions automatically bound to the current instance.

class X {
  fn = () => console.log(this.constructor.name)
}
const x = new X;
x.fn(); // regular call
const fn = x.fn;
fn(); // this is preserved
const fn2 = x.fn.bind(null);
fn2(); // cannot rebind this

Related