TLDR; Pas super nécessaire, mais aidera probablement à long terme, et il est plus précis de le faire.
REMARQUE: Beaucoup de choses ont été modifiées car ma réponse précédente a été écrite de manière confuse et contient des erreurs que j'ai manquées dans ma précipitation à répondre. Merci à ceux qui ont signalé des erreurs flagrantes.
Fondamentalement, il s'agit de câbler correctement le sous-classement en Javascript. Lorsque nous sous-classons, nous devons faire des choses amusantes pour nous assurer que la délégation prototypique fonctionne correctement, notamment en écrasant un prototype
objet. Le remplacement d'un prototype
objet inclut le constructor
, nous devons donc corriger la référence.
Voyons rapidement comment fonctionnent les «classes» dans ES5.
Disons que vous avez une fonction constructeur et son prototype:
//Constructor Function
var Person = function(name, age) {
this.name = name;
this.age = age;
}
//Prototype Object - shared between all instances of Person
Person.prototype = {
species: 'human',
}
Lorsque vous appelez le constructeur pour instancier, dites Adam
:
// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);
Le new
mot-clé invoqué avec 'Person' exécutera le constructeur Person avec quelques lignes de code supplémentaires:
function Person (name, age) {
// This additional line is automatically added by the keyword 'new'
// it sets up the relationship between the instance and the prototype object
// So that the instance will delegate to the Prototype object
this = Object.create(Person.prototype);
this.name = name;
this.age = age;
return this;
}
/* So 'adam' will be an object that looks like this:
* {
* name: 'Adam',
* age: 19
* }
*/
Si nous console.log(adam.species)
, la recherche échouera à l' adam
instance et recherchera la chaîne prototypique vers la sienne .prototype
, qui est Person.prototype
- et Person.prototype
possède une .species
propriété, la recherche réussira donc Person.prototype
. Il se connectera ensuite 'human'
.
Ici, le Person.prototype.constructor
pointera correctement Person
.
Alors maintenant, la partie intéressante, le soi-disant «sous-classement». Si nous voulons créer une Student
classe, c'est-à-dire une sous-classe de la Person
classe avec quelques modifications supplémentaires, nous devons nous assurer que les Student.prototype.constructor
points pointent vers Student pour plus de précision.
Il ne le fait pas tout seul. Lorsque vous sous-classe, le code ressemble à ceci:
var Student = function(name, age, school) {
// Calls the 'super' class, as every student is an instance of a Person
Person.call(this, name, age);
// This is what makes the Student instances different
this.school = school
}
var eve = new Student('Eve', 20, 'UCSF');
console.log(Student.prototype); // this will be an empty object: {}
Appeler new Student()
ici retournerait un objet avec toutes les propriétés que nous voulons. Ici, si nous vérifions eve instanceof Person
, cela reviendrait false
. Si nous essayons d'accéder eve.species
, il reviendrait undefined
.
En d'autres termes, nous devons câbler la délégation afin que eve instanceof Person
renvoie true et que les instances de Student
délégué soient correctement envoyées à Student.prototype
, puis Person.prototype
.
MAIS puisque nous l'appelons avec le new
mot - clé, rappelez-vous ce que cette invocation ajoute? Cela s'appellerait Object.create(Student.prototype)
, c'est ainsi que nous établissons cette relation de délégation entre Student
et Student.prototype
. Notez qu'en ce moment, Student.prototype
est vide. Ainsi, la recherche d' .species
une instance de Student
échouerait car elle délègue uniquement Student.prototype
et la .species
propriété n'existe pas Student.prototype
.
Lorsque nous nous attribuons Student.prototype
à Object.create(Person.prototype)
, Student.prototype
lui - même , les délégués sont Person.prototype
renvoyés et la recherche eve.species
revient human
comme prévu. Vraisemblablement, nous voudrions qu'il hérite de Student.prototype ET Person.prototype. Nous devons donc régler tout cela.
/* This sets up the prototypal delegation correctly
*so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
*This also allows us to add more things to Student.prototype
*that Person.prototype may not have
*So now a failed lookup on an instance of Student
*will first look at Student.prototype,
*and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);
Maintenant, la délégation fonctionne, mais nous remplaçons Student.prototype
par un de Person.prototype
. Donc, si nous appelons Student.prototype.constructor
, cela indiquerait au Person
lieu de Student
. C'est pourquoi nous devons y remédier.
// Now we fix what the .constructor property is pointing to
Student.prototype.constructor = Student
// If we check instanceof here
console.log(eve instanceof Person) // true
Dans ES5, notre constructor
propriété est une référence qui fait référence à une fonction que nous avons écrite avec l'intention d'être un «constructeur». Mis à part ce que le new
mot - clé nous donne, le constructeur est par ailleurs une fonction «simple».
Dans ES6, le constructor
est désormais intégré dans la façon dont nous écrivons les classes - comme dans, il est fourni comme méthode lorsque nous déclarons une classe. Il s'agit simplement de sucre syntaxique, mais il nous offre certaines commodités comme l'accès à un super
lorsque nous étendons une classe existante. Nous écririons donc le code ci-dessus comme ceci:
class Person {
// constructor function here
constructor(name, age) {
this.name = name;
this.age = age;
}
// static getter instead of a static property
static get species() {
return 'human';
}
}
class Student extends Person {
constructor(name, age, school) {
// calling the superclass constructor
super(name, age);
this.school = school;
}
}