|
|
|
|
|
by pluma
3034 days ago
|
|
The class system is a bit hit and miss. In practice you will be fine if you're using classes and you will be fine if you use prototypes but you should avoid mixing them directly. Strictly speaking, there is no essential difference because it's entirely possible to implement classical inheritance using prototypal inheritance -- just not the other way around. There's no classical equivalent of this, for example: foo = {hello: "world"};
bar = Object.create(foo);
console.log(bar.hello); // "world"
Note how bar has no "hello" property, so the property is looked up on its prototype, foo.Your code uses the "new" keyword. That keyword is pretty much emulating classical behaviour on top of the prototypal inheritance. The following two examples are equivalent: foo = new Foo(1, 2, 3);
and foo = Object.create(Foo.prototype);
Foo.call(foo, 1, 2, 3);
Every function implicitly has a "prototype" property which is set to an object with a "constructor" property set to the function itself.Object.create creates a new object and sets its prototype (the internal [[Prototype]] property, not the "prototype" property on the function) to its argument. There's a bit more going on underneath (mostly to allow certain optimisations) but conceptually even the "new" keyword is just syntactic sugar. |
|
1) Prototypes with constructors using the "new" keyword (ES3+):
2) Plain prototypes with Object.create (ES5+): 3) Classes (ES2015+): 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'