Reputation: 18169
I discovered that ES2015 classes prevent setting (redefining) their prototype.
It is often claimed that ES2015 classes are just "syntactic sugar" on top of ES5 constructor functions and prototype based inheritance.
But this is a difference in behavior...
Is this behavior part of the ES2015 specification? I did not find any documentation about this ...
The following examples illustrate the difference:
function Pet() {}
Pet.prototype.eat = () => {
console.log('Pet is eating ...');
}
Pet.prototype = {eat: function(){console.log('Pet is REALLY eating ...')}};
const pet = new Pet();
pet.eat(); // -> Pet is REALLY eating ...
console.log(Object.getOwnPropertyDescriptor(Pet, 'prototype'));
=> Redefining prototype of Pet works
class Pet {
eat() {
console.log('Pet is eating ...');
}
}
Pet.prototype = {eat: function(){console.log('Pet is REALLY eating ...')}};
const pet = new Pet();
pet.eat(); // -> Pet is eating ...
console.log(Object.getOwnPropertyDescriptor(Pet, 'prototype'));
=> Redefining prototype of Pet does not work
Any pointers to documentation of this behavior would be appreciated ...
Upvotes: 6
Views: 284
Reputation: 707318
The difference here is that when created with class
, the prototype
object is set to writable: false
so that you can't replace Pet.prototype
with assignment. You can see that difference in your Object.getOwnPropertyDescriptor()
call when you remove strict mode.
The OP's first code example shows this:
{
...
"writable": true,
"enumerable": false,
"configurable": false
}
The OP's second code example shows this:
{
...
"writable": false,
"enumerable": false,
"configurable": false
}
The writable
property determines whether the property Pet.prototype
can be assigned a new value or not (e.g. replaced with a new object). A false
value means that you cannot replace Pet.prototype
using assignment. So, when your code tries to do that, it fails.
You are still free to add or remove individual properties from the prototype object, just not replace the entire object. This makes some sense to me because replacing the entire prototype object undoes the entire class
definition. You could probably change the Pet.prototype
object to be writable if you had a real world reason to replace it.
This is described in the ES 2015 specification. In 14.5.14 Runtime Semantics: ClassDefinitionEvaluation, at step 16, it does this:
- Perform MakeConstructor(F, false, proto).
That false
argument is making the prototype that is created non-writable (so it can't be replaced).
And, if you then look at 9.2.8 MakeConstructor(), you see this in step 6:
Let status be DefinePropertyOrThrow(F, "prototype", PropertyDescriptor{[[Value]]: prototype, [[Writable]]: writablePrototype, [[Enumerable]]: false, [[Configurable]]: false}).
Where the writable
attribute is getting the false
value that was previously passed to to it.
It is often claimed that ES2015 classes are just "syntactic sugar" on top of ES5 constructor functions and prototype based inheritance. But this is a difference in behavior...
While the general operation of the methods defined in the class
is pretty much the same, this other answer describes several other differences between using class
versus manually assigning to the prototype: In javascript, what are the differences between a class and a constructor?
Upvotes: 8