3

The question is related to the resolution of the this operator in Javascript classes. NodeJS now supports ES6 classes. The problem faced during this is that when a new instance of the class is created and assigned to a variable everything works fine, but if you assign a particular class method to a variable, the scope of the this variable is lost.

class A {

    publicMethod() {
        console.log('Public Method');
        console.log('Calling Private Method');
        this._privateMethod();   
    }

    _privateMethod() {
        console.log('Private Method');
    }

}

class B extends A {
    constructor() {
        super();
        return  {
            publicMethod: this.publicMethod
        }
    }
}

class C extends A {
    constructor() {
        super();
        return  {
            publicMethod: this.publicMethod.bind(this)
        }
    }
}

The question is two parts:

  1. What is the performance affect of using bind on class methods? Think about it being used as a practice across the entire scope of application. (when accessing publicMethod from B, it throws an error, but not in case of A or C)
  2. Is it a good practice, in order to support visibility, instead of returning nothing from the class constructor, return a few methods, which is meant for the public, hence enabling class visibility. (As in B and C)

1 Answers1

1

Explicit Return in Constructor

An explicit return in a constructor returns an object which no longer have the original prototype. If you handled it so that the original prototype is used, then this is OK.

However, it's unusual and I can't really think of how this could help you. Because, at that point, you can just use a regular function -- the ES6 class syntax really isn't helping you anymore.

Arrow Functions

I think it'd be better if you just did:

class B2 extends A {
    constructor() {
        super();
        this.publicMethod = () => super.publicMethod()
    }
}

Which is nearly identical to using bind, but I think it's cleaner to use arrow functions.

Class Properties

Alternatively, babel has support for class properties which allows you to write code which transpiles to just setting a field with an arrow function (like B2) that looks like so:

class A {
    constructor() {
        this.i = 0
    }

    publicMethod = () => this.i++
}

Don't use bind/arrow functions

You could also design your application so that you don't need to use bind/arrow functions and you'd have the ideal situation like so:

class B3 extends A {
    constructor() {
        super();
    }

    publicMethod() {
        super.publicMethod();
    }
}

Wrap calls rather than methods

If you're not using bind/arrow functions you will, at times, still need to make sure that the context is preserved.

In my opinion, it's more consistent to just wrap the context of the method call within an arrow function or bind because it's the caller that ultimately determines the context and it should be the caller's duty to deal with it rather than figuring out if the author set things up correctly for you. You can do that like so:

const a = new A();
const callback = () => a.method();
el.addEventListener('event', callback);

You can also achieve the same effect by using a higher order function which takes in the context to achieve a similar effect without using bind/arrow functions.

function wrapCall(a) {
    return function(event) { a.method(event) };
}

const a = new A();
const callback = wrapCall(a);
el.addEventListener('event', callback);

Performance

Using bind on a function is not particularly expensive. But it does result in more function references as each instance of the object gets a property with the function in it based on the original. Regular methods allow you to share the function amongst all instances since it's stored in the object's prototype.

If you have to create new functions each time something happens, for example, during a React render, you can create lots of functions unnecessarily. It's usually cheaper to use bind/arrow functions and just store it as a field or somewhere that's accessible. However, if you only need to create one instance of a function to bind to a DOM, it's cheaper to just use an arrow function or bind when you're adding the event like in the above example.

Overhead of arrow and bounded function

Technically, an arrow function is slower than a bounded function and a regular function is faster than both. However, this is sort of one of those things that doesn't really matter in the real world -- your function call overhead probably isn't gonna make or break your application performance. Using unnecessary memory by creating extra references is a larger problem, (albeit only slightly) and is worth more your time.

Conclusion

Unfortunately, in the JS world, best practices are rather ill defined and there's lots of ways to do the same thing; none of which are clearly better than another. You're probably just going to have to make up your own mind on a situational basis.

There are, however, some resources that discuss the topic that I came across by more prominent individuals which you can take a look at. They're focused on react, but I believe the principle still applies if you read in between the lines.

Kevin Raoofi
  • 211
  • 1
  • 9