Hacker News new | ask | show | jobs
by pluma 3034 days ago
To be clear: in practice there are three ways to use inheritance in JS:

1) Prototypes with constructors using the "new" keyword (ES3+):

    function Foo (name) {
      this.name = name;
    }
    Foo.prototype.hello = function () {
      return `Hello ${this.name}!`;
    };
    let foo = new Foo("world");
    console.log(foo.hello()); // Hello world!
2) Plain prototypes with Object.create (ES5+):

    let bar = {
      name: 'world',
      hello: function () {
        return `Hello ${this.name}!`;
      }
    };
    let baz = Object.create(bar);
    baz.name = 'Wisconsin';
    console.log(bar.hello()); // Hello world!
    console.log(baz.hello()); // Hello Wisconsin!
3) Classes (ES2015+):

    class Qux {
      constructor (name) {
        this.name = name;
      }
      hello () {
        return `Hello ${this.name}!`;
      }
    }
    let qux = new Qux("world");
    console.log(qux.hello()); // Hello world!
Note how all three examples do (roughly) the same thing but in slightly different ways.

Inheritance in #1 is a bit awkward (which is why before ES2015 there were myriads of different inheritance libraries).

In #2 it's fairly straightforward but type checks are unintuitive because there really aren't any types involved (instanceof requires a constructor).

In #3 both inheritance and type checks are fairly straightforward but it also uses more abstractions, which can make it more difficult to understand conceptually (especially when coming from a language like Java or C# which is syntactically similar).

IMO #1 is the worst of both worlds because the behaviour of the "new" keyword and the special "prototype" property are difficult to understand and the constructor looks like a regular function but expects to be called in a special way (using the "new" keyword) and will break in unexpected ways if called as a normal function:

> TypeError: Cannot set property 'name' of undefined

(or worse: outside strict mode it might not fail and actually try to write to the global scope -- so remember to always "use strict")

While the value of a class is also a constructor function, calling it without "new" will break with a distinct error that makes the mistake obvious and easy to understand:

> TypeError: Class constructor Qux cannot be invoked without 'new'

1 comments

Thanks :-)

I've never used Object.create for inheritance (although I heard it was a favorite of Douglas Crockford's at some point). And the syntactic sugar of es2015 classes is so appealing I never wrote constructor functions on a regular basis.

What's so conceptually difficult about es2015 classes that confuses people? They always felt intuitive to me. A bit restrictive perhaps, because you don't have private methods, or instance properties, and if you want to call super in a method you have to do it before anything else, but other than that — nothing especially confusing.