TLDR; Non super necessario, ma probabilmente aiuterà a lungo termine, ed è più preciso farlo.
NOTA: Molto modificato in quanto la mia risposta precedente è stata scritta in modo confuso e presentava alcuni errori a cui ho perso la fretta di rispondere. Grazie a coloro che hanno segnalato alcuni errori eclatanti.
Fondamentalmente, si tratta di collegare correttamente la sottoclasse in Javascript. Quando effettuiamo la sottoclasse, dobbiamo fare alcune cose funky per assicurarci che la delegazione prototipo funzioni correttamente, inclusa la sovrascrittura di un prototype
oggetto. La sovrascrittura di un prototype
oggetto include il constructor
, quindi è necessario correggere il riferimento.
Vediamo rapidamente come funzionano le "classi" in ES5.
Supponiamo che tu abbia una funzione di costruzione e il suo prototipo:
//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',
}
Quando chiami il costruttore per creare un'istanza, dì Adam
:
// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);
La new
parola chiave invocata con "Person" eseguirà sostanzialmente il costruttore Person con alcune righe di codice aggiuntive:
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
* }
*/
Se noi console.log(adam.species)
, la ricerca fallirà adam
nell'istanza, e cercheremo la catena prototipo alla sua .prototype
, che è Person.prototype
- e Person.prototype
ha una .species
proprietà, quindi la ricerca riuscirà Person.prototype
. Quindi registrerà 'human'
.
Qui, Person.prototype.constructor
indicherà correttamente Person
.
Quindi ora la parte interessante, la cosiddetta "sottoclasse". Se vogliamo creare una Student
classe, che è una sottoclasse della Person
classe con alcune modifiche aggiuntive, dovremo assicurarci che i Student.prototype.constructor
punti a Studente siano precisi.
Non lo fa da solo. Quando si esegue la sottoclasse, il codice è simile al seguente:
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: {}
Chiamare new Student()
qui restituirebbe un oggetto con tutte le proprietà che vogliamo. Qui, se controlliamo eve instanceof Person
, ritornerebbe false
. Se proviamo ad accedere eve.species
, ritornerebbeundefined
.
In altre parole, dobbiamo collegare la delega in modo che eve instanceof Person
ritorni vero e che le istanze del Student
delegato siano correttamente Student.prototype
e quindi Person.prototype
.
MA poiché lo chiamiamo con la new
parola chiave, ricordi cosa aggiunge quell'invocazione? Si chiamerebbe Object.create(Student.prototype)
, ed è così che abbiamo creato quella relazione delegativa tra Student
e Student.prototype
. Si noti che in questo momento, Student.prototype
è vuoto. Quindi cercare .species
un'istanza di Student
fallirebbe in quanto delega solo Student.prototype
e la .species
proprietà non esisteStudent.prototype
.
Quando lo assegniamo Student.prototype
a noi stessi Object.create(Person.prototype)
, Student.prototype
poi si delega Person.prototype
e guardando in alto eve.species
tornerà human
come ci aspettiamo. Presumibilmente vorremmo che ereditasse da Student.prototype AND Person.prototype. Quindi dobbiamo risolvere tutto ciò.
/* 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);
Ora la delegazione funziona, ma stiamo sovrascrivendo Student.prototype
con un di Person.prototype
. Quindi, se chiamiamo Student.prototype.constructor
, indicherebbe Person
invece di Student
. Questo è il motivo per cui dobbiamo ripararlo.
// 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
In ES5, la nostra constructor
proprietà è un riferimento che si riferisce a una funzione che abbiamo scritto con l'intento di essere un "costruttore". A parte ciò che new
ci fornisce la parola chiave, il costruttore è altrimenti una funzione "semplice".
In ES6, constructor
ora è integrato nel modo in cui scriviamo le classi - come in, viene fornito come metodo quando dichiariamo una classe. Questo è semplicemente zucchero sintattico, ma ci concede alcune comodità come l'accesso a una super
quando stiamo estendendo una classe esistente. Quindi scriveremo il codice sopra in questo modo:
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;
}
}