| 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' |
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.