Dlaczego konieczne jest ustawienie konstruktora prototypów?

294

W części dotyczącej dziedziczenia w artykule MDN Wstęp do JavaScript zorientowanego obiektowo zauważyłem, że ustawili prototype.constructor:

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;  

Czy to ma jakiś ważny cel? Czy można to pominąć?

trinth
źródło
23
Cieszę się, że o to zapytałeś: wczoraj przeczytałem tę samą dokumentację i byłem ciekawy uzasadnienia jawnego ustawienia konstruktora.
Wylie,
6
Musiałem tylko to zaznaczyć, to pytanie jest teraz połączone w artykule, który podłączyłeś!
Marie,
7
nic nie jest konieczne
nothingisnecessary
1
subclass.prototype.constructorWskaże parent_class, jeśli nie pisać subclass.prototype.constructor = subclass; Oznacza to, że subclass.prototype.constructor()bezpośrednie użycie spowoduje nieoczekiwany wynik.
KuanYu Chu

Odpowiedzi:

263

Nie zawsze jest to konieczne, ale ma swoje zastosowania. Załóżmy, że chcieliśmy stworzyć metodę kopiowania w Personklasie bazowej . Lubię to:

// define the Person Class  
function Person(name) {
    this.name = name;
}  

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  

// define the Student class  
function Student(name) {  
    Person.call(this, name);
}  

// inherit Person  
Student.prototype = Object.create(Person.prototype);

Co się teraz stanie, gdy utworzymy nowy Studenti skopiujemy go?

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => false

Kopia nie jest instancją Student. Wynika to z faktu, że (bez wyraźnych kontroli) nie moglibyśmy zwrócić Studentkopii z klasy „podstawowej”. Możemy tylko zwrócić Person. Gdybyśmy jednak zresetowali konstruktor:

// correct the constructor pointer because it points to Person  
Student.prototype.constructor = Student;

... wtedy wszystko działa zgodnie z oczekiwaniami:

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => true
Wayne
źródło
34
Uwaga: constructorAtrybut nie ma żadnego specjalnego znaczenia w JS, więc równie dobrze możesz go wywołać bananashake. Jedyna różnica polega na tym, że silnik uruchamia się automatycznie constructorza f.prototypekażdym razem, gdy deklarujesz funkcję f. Można go jednak zastąpić w dowolnym momencie.
user123444555621,
58
@ Pumbaa80 - Rozumiem, ale fakt, że silnik automatycznie inicjuje się constructoroznacza, że ma on specjalne znaczenie w JS, z definicji.
Wayne,
13
Chcę tylko wyjaśnić, że powodem, dla którego powiedziałeś, że działa, jest to, że używasz return new this.constructor(this.name);zamiast return new Person(this.name);. Ponieważ this.constructorjest to Studentfunkcja (ponieważ została ustawiona za pomocą Student.prototype.constructor = Student;), copyfunkcja w końcu wywołuje Studentfunkcję. Nie jestem pewien, jaki był twój zamiar z //just as badkomentarzem.
CEGRD,
12
@lwburk co rozumiesz przez „// równie zły”?
CEGRD,
6
Myślę, że rozumiem. Ale co jeśli Studentkonstruktor dodał dodatkowy argument, taki jak Student(name, id):? Czy musimy wówczas zastąpić copyfunkcję, wywołać Personwersję z jej wnętrza, a następnie skopiować dodatkową idwłaściwość?
snapfractalpop
76

Czy to ma jakiś ważny cel?

Tak i nie.

W ES5 i wcześniejszych sam JavaScript constructordo niczego nie używał . Określił, że domyślny obiekt we prototypewłaściwości funkcji będzie go zawierał i że będzie odwoływał się do funkcji i to było to . Nic więcej w specyfikacji tego nie dotyczyło.

Zmieniło się to w ES2015 (ES6), która zaczęła używać go w odniesieniu do hierarchii dziedziczenia. Na przykład Promise#thenużywa constructorwłaściwości obietnicy, do której ją wywołujesz (przez SpeciesConstructor ), podczas budowania nowej obietnicy zwrotu. Jest także zaangażowany w tablice podtypów (przez ArraySpeciesCreate ).

Poza samym językiem czasami ludzie używali go, próbując budować ogólne funkcje „klonowania” lub po prostu, gdy chcieli odwołać się do tego, co według nich byłoby funkcją konstruktora obiektu. Z mojego doświadczenia wynika, że ​​korzystanie z niego jest rzadkie, ale czasami ludzie go używają.

Czy można to pominąć?

Jest tam domyślnie, wystarczy go odłożyć, gdy zastąpisz obiekt we prototypewłaściwości funkcji :

Student.prototype = Object.create(Person.prototype);

Jeśli tego nie zrobisz:

Student.prototype.constructor = Student;

... Student.prototype.constructordziedziczy po Person.prototypetym (prawdopodobnie) constructor = Person. To jest mylące. I oczywiście, jeśli podklasujesz coś, co go używa (jak Promiselub Array) i nie używaszclass ¹ (który obsługuje to za Ciebie), upewnij się, że ustawiłeś to poprawnie. Więc w zasadzie: to dobry pomysł.

Jest w porządku, jeśli nic w twoim kodzie (lub kodzie biblioteki, którego używasz) go nie używa. Zawsze upewniłem się, że jest prawidłowo podłączony.

Oczywiście, ze słowem kluczowym ES2015 (alias ES6) class, przez większość czasu byśmy go użyli, nie musimy już tego robić, ponieważ jest to obsługiwane dla nas, kiedy to robimy

class Student extends Person {
}

¹ „... jeśli podklasujesz coś, co go używa (jak Promiselub Array) i nie używasz class...”  - Można to zrobić, ale to prawdziwy ból (i trochę głupi). Musisz użyć Reflect.construct.

TJ Crowder
źródło
12

TLDR; Nie jest to konieczne, ale prawdopodobnie pomoże na dłuższą metę i jest to bardziej dokładne.

UWAGA: Wiele zredagowałem, ponieważ moja poprzednia odpowiedź była myląco napisana i zawierała błędy, których nie spóźniłem się z odpowiedzią. Dzięki tym, którzy wskazali na pewne rażące błędy.

Zasadniczo ma to na celu prawidłowe podklasowanie w JavaScript. Kiedy dokonujemy podklasy, musimy zrobić kilka dziwnych rzeczy, aby upewnić się, że delegacja prototypowa działa poprawnie, w tym nadpisanie prototypeobiektu. Nadpisanie prototypeobiektu obejmujeconstructor , dlatego musimy naprawić odwołanie.

Zobaczmy, jak działają „klasy” w ES5.

Powiedzmy, że masz funkcję konstruktora i jej prototyp:

//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',
}

Po wywołaniu konstruktora w celu utworzenia wystąpienia, powiedz Adam:

// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);

newKluczowe wywołany z „osoba” w zasadzie będzie działać konstruktora osoba z kilku dodatkowych linii kodu:

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
 * }
 */

Gdybyśmy console.log(adam.species), odnośnika zawiedzie na adamprzykład, i patrzeć prototypal łańcuch do swojego .prototype, co jest Person.prototype- i Person.prototype ma na .specieswłasność, więc wyszukiwanie na uda Person.prototype. Następnie się zaloguje'human' .

Tutaj Person.prototype.constructorpoprawnie wskaże Person.

Więc teraz interesująca część, tak zwane „podklasowanie”. Jeśli chcemy stworzyć Studentklasę, która jest podklasą Personklasy z pewnymi dodatkowymi zmianami, musimy upewnić się, że Student.prototype.constructorpunkty wskazują Uczniowi na dokładność.

Sam tego nie robi. Podklasę kod wygląda następująco:

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: {}

Wywołanie new Student()tutaj zwróci obiekt ze wszystkimi pożądanymi właściwościami. Tutaj, jeśli sprawdzimy eve instanceof Person, wróci false. Jeśli spróbujemy uzyskać dostęp eve.species, nastąpi powrót undefined.

Innymi słowy, musimy uporządkować delegację, aby eve instanceof Personzwracała wartość true, a instancje Studentdelegowania były poprawnie przesyłane do Student.prototype, a następnie Person.prototype.

ALE, ponieważ nazywamy to newsłowem kluczowym, pamiętasz, co dodaje to wywołanie? To by zadzwoniło Object.create(Student.prototype), w ten sposób ustanowiliśmy relacje delegacyjne pomiędzy Studenti Student.prototype. Zauważ, że teraz Student.prototypejest pusty. Dlatego wyszukiwanie .speciesinstancji Studentnie powiedzie się, ponieważ deleguje ją tylko Student.prototype , a .specieswłaściwość nie istnieje Student.prototype.

Kiedy przydzielimy Student.prototypedo Object.create(Person.prototype), Student.prototypesama deleguje się do Person.prototype, a wyszukiwanie eve.specieswróci, humanjak się spodziewamy. Przypuszczalnie chcielibyśmy, aby odziedziczył po Student.prototype AND Person.prototype. Musimy to wszystko naprawić.

/* 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);

Teraz delegacja działa, ale zastępujemy Student.prototypePerson.prototype. Więc jeśli zadzwonimy Student.prototype.constructor, wskazywałoby to na Personzamiast Student. To dlatego musimy go naprawić.

// 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

W ES5 nasza constructorwłaściwość jest referencją odnoszącą się do funkcji, którą napisaliśmy z zamiarem bycia „konstruktorem”. Oprócz tego, co newdaje nam słowo kluczowe, konstruktor jest poza tym „prostą” funkcją.

W ES6 constructorjest teraz wbudowany w sposób, w jaki piszemy klasy - podobnie jak w, jest on dostarczany jako metoda, gdy deklarujemy klasę. Jest to po prostu cukier składniowy, ale zapewnia nam pewne udogodnienia, takie jak dostęp do superkiedy rozszerzamy istniejącą klasę. Więc napisalibyśmy powyższy kod w ten sposób:

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;
   }
}
bthehuman
źródło
eve instanceof Studentpowrócił true. Wyjaśnienie można znaleźć na stackoverflow.com/questions/35537995/ ... Również kiedy mówisz o which is, at the moment, nothingczym mówisz? Każda funkcja ma prototyp, więc jeśli sprawdzę Student.prototype, czy to coś.
Aseem Bansal
Mój błąd. Powinien był przeczytać „eve instanceof Person”, który zwróciłby wartość false. Zmienię tę część. Masz rację, że każda funkcja ma właściwość prototypu. Jednak , bez przypisywania prototyp do Object.create(Person.prototype)The Student.prototypejest pusty. Więc jeśli się zalogujemy eve.species, nie deleguje się odpowiednio do swojej nadklasy Person i nie będzie się logował 'human'. Przypuszczalnie chcemy, aby każda podklasa dziedziczyła po prototypie, a także prototypie super.
bthehuman
Aby wyjaśnić, which is, at the moment, nothingmiałem na myśli, że Student.prototypeobiekt jest pusty.
bthehuman
Więcej na temat prototypu: Bez przypisania Student.prototypedo Object.create(Person.prototype)- co, jeśli pamiętasz, w ten sam sposób wszystkie instancje Osoby są skonfigurowane do delegowania do Person.prototype- wyszukiwanie właściwości w instancji Studentkomendy delegowanej byłoby tylko Student.prototype . Więc eve.speciesnie powiedzie się jego wyszukiwanie. Jeśli go przypisamy, to Student.prototypesam się deleguje Person.prototype, a wyszukiwanie eve.specieswróci human.
bthehuman
Wydaje się, że jest tu kilka rzeczy błędnych: „Jest to konieczne, gdy próbujesz emulować„ podklasę ”[...], aby sprawdzić, czy instancja jest instanceKonstruktorem„ podklasy ”, będzie dokładna”. Nie, instanceofnie używa constructor. „Jednakże, jeśli spojrzymy na konstruktora .prototype. studenta, nadal wskazywałby na Osoba” Nie, to będzie Student. Nie rozumiem sensu tego przykładu. Wywołanie funkcji w konstruktorze nie jest dziedziczeniem. „W ES6 konstruktor jest teraz rzeczywistą funkcją zamiast odwołania do funkcji” Uh co?
Felix Kling
10

Nie zgodziłbym się. Nie trzeba ustawiać prototypu. Weź ten sam kod, ale usuń linię prototype.constructor. Czy coś się zmienia? Nie. Teraz wprowadź następujące zmiany:

Person = function () {
    this.favoriteColor = 'black';
}

Student = function () {
    Person.call(this);
    this.favoriteColor = 'blue';
}

a na końcu kodu testowego ...

alert(student1.favoriteColor);

Kolor będzie niebieski.

Z mojego doświadczenia wynika, że ​​zmiana na prototype.constructor nie robi wiele, chyba że robisz bardzo konkretne, bardzo skomplikowane rzeczy, które i tak prawdopodobnie nie są dobrą praktyką :)

Edycja: Po przeszukaniu sieci i przeprowadzeniu eksperymentów, wygląda na to, że ludzie ustawili konstruktor tak, aby „wyglądał” jak ten, który jest konstruowany za pomocą „nowego”. Chyba twierdzę, że problem polega na tym, że javascript jest językiem prototypowym - nie ma czegoś takiego jak dziedziczenie. Ale większość programistów wywodzi się ze środowiska programowania, które popycha dziedziczenie jako „drogę”. Wymyślamy różne rzeczy, aby ten prototypowy język stał się językiem „klasycznym”, na przykład rozszerzając „klasy”. Naprawdę, w podanym przez nich przykładzie, nowy uczeń to osoba - to nie „rozciąga się” od innego ucznia. Uczeń jest o tej osobie, i cokolwiek to jest. Rozszerz ucznia i cokolwiek innego

Crockford jest trochę szalony i nadgorliwy, ale poczytaj kilka rzeczy na temat tego, co napisał ... sprawi, że spojrzysz na to inaczej.

Stephen
źródło
8
To nie dziedziczy łańcucha prototypów.
Cypher,
1
@Cypher powolny klaszcz wita w rozmowie, cztery lata później. Tak, łańcuch prototypów jest dziedziczony, niezależnie od tego, czy zastąpisz prototype.constructor. Spróbuj to przetestować.
Stephen
7
Brakuje kodu, który dziedziczy prototyp. Witamy w Internecie.
Cypher
1
Fragment kodu @Cypher został oparty na kodzie w połączonym artykule. Zapraszamy do przeczytania pytania w całości. O. Czekać.
Stephen
1
@macher Miałem to na myśli jako klasyczne dziedzictwo. Zły wybór sformułowania z mojej strony.
Stephen
9

Ma to ogromną pułapkę, że jeśli napisałeś

Student.prototype.constructor = Student;

ale jeśli byłby Nauczyciel, którego prototypem był także Osoba, a ty napisałeś

Teacher.prototype.constructor = Teacher;

wtedy Konstruktor Ucznia jest teraz Nauczycielem!

Edycja: Możesz tego uniknąć, upewniając się, że ustawiłeś prototypy Ucznia i Nauczyciela przy użyciu nowych instancji klasy Person utworzonych za pomocą Object.create, jak w przykładzie Mozilla.

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);
James D.
źródło
1
Student.prototype = Object.create(...)zakłada się w tym pytaniu. Ta odpowiedź dodaje jedynie możliwe zamieszanie.
André Chalella,
3
@ AndréNeves Uważam, że ta odpowiedź jest pomocna. Object.create(...)jest używany w artykule MDN, który zrodził pytanie, ale nie w samym pytaniu. Jestem pewien, że wiele osób nie klika.
Alex Ross
Artykuł, do którego odsyłamy w pytaniu, używa Object.create (). Ta odpowiedź i Edycja odpowiedzi nie są tak naprawdę istotne i co najmniej mylące jest to :-)
Drenai
1
Szersze jest to, że istnieją luki, które będą zaczepiać ludzi nowych w prototypach Javascript. Jeśli dyskutujemy w 2016 roku, naprawdę powinieneś używać klas ES6, Babel i / lub maszynopisu. Ale jeśli naprawdę chcesz ręcznie budować klasy w ten sposób, pomaga zrozumieć, jak naprawdę działają prototypowe łańcuchy, aby wykorzystać ich moc. Możesz użyć dowolnego obiektu jako prototypu, a może nie chcesz nowego osobnego obiektu. Ponadto, zanim HTML 5 był w pełni rozpowszechniony, Object.create nie zawsze był dostępny, więc łatwiej było niepoprawnie skonfigurować klasę.
James D
5

Do tej pory zamieszanie wciąż istnieje.

Zgodnie z oryginalnym przykładem, ponieważ masz istniejący obiekt student1jako:

var student1 = new Student("Janet", "Applied Physics");

Załóżmy, że nie chcesz wiedzieć, jak student1jest tworzony, po prostu chcesz inny obiekt taki jak ten, możesz użyć właściwości konstruktora, student1np .:

var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");

Tutaj nie będzie można pobrać właściwości, Studentjeśli właściwość konstruktora nie jest ustawiona. Raczej stworzy Personobiekt.

Mahawir
źródło
2

Mam dobry przykład kodu, dlaczego tak naprawdę konieczne jest ustawienie konstruktora prototypu ..

function CarFactory(name){ 
   this.name=name;  
} 
CarFactory.prototype.CreateNewCar = function(){ 
    return new this.constructor("New Car "+ this.name); 
} 
CarFactory.prototype.toString=function(){ 
    return 'Car Factory ' + this.name;
} 

AudiFactory.prototype = new CarFactory();      // Here's where the inheritance occurs 
AudiFactory.prototype.constructor=AudiFactory;       // Otherwise instances of Audi would have a constructor of Car 

function AudiFactory(name){ 
    this.name=name;
} 

AudiFactory.prototype.toString=function(){ 
    return 'Audi Factory ' + this.name;
} 

var myAudiFactory = new AudiFactory('');
  alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');            

var newCar =  myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory 
alert(newCar); 

/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class )..   Dont we want our new car from Audi factory ???? 
*/
użytkownik3877965
źródło
Twoja createNewCarmetoda polega na tworzeniu fabryk !? Wygląda na to, że powinien był zostać użyty var audiFactory = new CarFactory("Audi")zamiast dziedziczenia.
Bergi,
Twój przykład używa this.constructorwewnętrznie, więc nic dziwnego, że trzeba go ustawić. Czy masz jakiś przykład bez niego?
Dmitrij Zajcew
1

W dzisiejszych czasach nie ma potrzeby używania „klas” funkcji słodzonych ani używania „nowych”. Użyj literałów obiektowych.

Prototyp Object jest już „klasą”. Po zdefiniowaniu literału obiektu jest to już instancja prototypowego obiektu. Mogą również działać jako prototyp innego obiektu itp.

const Person = {
  name: '[Person.name]',
  greeting: function() {
    console.log( `My name is ${ this.name || '[Name not assigned]' }` );
  }
};
// Person.greeting = function() {...} // or define outside the obj if you must

// Object.create version
const john = Object.create( Person );
john.name = 'John';
console.log( john.name ); // John
john.greeting(); // My name is John 
// Define new greeting method
john.greeting = function() {
    console.log( `Hi, my name is ${ this.name }` )
};
john.greeting(); // Hi, my name is John

// Object.assign version
const jane = Object.assign( Person, { name: 'Jane' } );
console.log( jane.name ); // Jane
// Original greeting
jane.greeting(); // My name is Jane 

// Original Person obj is unaffected
console.log( Person.name ); // [Person.name]
console.log( Person.greeting() ); // My name is [Person.name]

Warto przeczytać :

Klasowe języki obiektowe, takie jak Java i C ++, oparte są na koncepcji dwóch odrębnych jednostek: klas i instancji.

...

Język oparty na prototypach, taki jak JavaScript, nie czyni tego rozróżnienia: po prostu zawiera obiekty. Język oparty na prototypach ma pojęcie obiektu prototypowego, obiektu używanego jako szablon, z którego można uzyskać początkowe właściwości nowego obiektu. Każdy obiekt może określać własne właściwości, zarówno podczas jego tworzenia, jak i w czasie wykonywania. Ponadto dowolny obiekt może być skojarzony jako prototyp innego obiektu, umożliwiając drugiemu obiektowi współdzielenie właściwości pierwszego obiektu

ucsarge
źródło
1

Jest to konieczne, gdy potrzebujesz alternatywy dla toStringbez monkeypatchingu:

//Local
foo = [];
foo.toUpperCase = String(foo).toUpperCase;
foo.push("a");
foo.toUpperCase();

//Global
foo = [];
window.toUpperCase = function (obj) {return String(obj).toUpperCase();}
foo.push("a");
toUpperCase(foo);

//Prototype
foo = [];
Array.prototype.toUpperCase = String.prototype.toUpperCase;
foo.push("a");
foo.toUpperCase();

//toString alternative via Prototype constructor
foo = [];
Array.prototype.constructor = String.prototype.toUpperCase;
foo.push("a,b");
foo.constructor();

//toString override
var foo = [];
foo.push("a");
var bar = String(foo);
foo.toString = function() { return bar.toUpperCase(); }
foo.toString();

//Object prototype as a function
Math.prototype = function(char){return Math.prototype[char]};
Math.prototype.constructor = function() 
  {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  
  while (i < max) 
    {
    Math.prototype[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);

    i = i + 1;
    }    
  }

Math.prototype.constructor();
console.log(Math.prototype("a") );
console.log(Math.prototype["a"] );
console.log(Math.prototype("a") === Math.prototype["a"]);

Paul Sweatte
źródło
Co to ma zrobić? foo.constructor()??
Ry-
0

EDYCJA, tak naprawdę się myliłem. Komentowanie wyjścia nie zmienia jego zachowania. (Przetestowałem to)


Tak, jest to konieczne. Kiedy to zrobisz

Student.prototype = new Person();  

Student.prototype.constructorstaje się Person. Dlatego wywołanie Student()zwróci obiekt utworzony przez Person. Jeśli to zrobisz

Student.prototype.constructor = Student; 

Student.prototype.constructorresetuje się z powrotem do Student. Teraz, kiedy Student()go wywołujesz , wykonuje Student, który wywołuje konstruktor nadrzędny Parent(), zwraca poprawnie odziedziczony obiekt. Jeśli nie zresetujesz się Student.prototype.constructorprzed wywołaniem go, otrzymasz obiekt, który nie będzie miał żadnej z ustawionych właściwości Student().

niewidzialny bob
źródło
3
Prototypowa konstrukcja może stać się osobą, ale jest to właściwe, ponieważ dziedziczy wszystkie właściwości i metody od Osoby. Utworzenie nowego Studenta () bez ustawienia prototype.constructor odpowiednio wywołuje własnego konstruktora.
Stephen
0

Biorąc pod uwagę prostą funkcję konstruktora:

function Person(){
    this.name = 'test';
}


console.log(Person.prototype.constructor) // function Person(){...}

Person.prototype = { //constructor in this case is Object
    sayName: function(){
        return this.name;
    }
}

var person = new Person();
console.log(person instanceof Person); //true
console.log(person.sayName()); //test
console.log(Person.prototype.constructor) // function Object(){...}

Domyślnie (ze specyfikacji https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ) wszystkie prototypy automatycznie otrzymują właściwość o nazwie konstruktor, która wskazuje na funkcję na która jest własnością. W zależności od konstruktora do prototypu mogą zostać dodane inne właściwości i metody, co nie jest bardzo powszechną praktyką, ale nadal jest dozwolone dla rozszerzeń.

Odpowiadając po prostu: musimy upewnić się, że wartość w prototype.constructor jest poprawnie ustawiona zgodnie z założeniami specyfikacji.

Czy zawsze musimy poprawnie ustawiać tę wartość? Pomaga w debugowaniu i zapewnia zgodność wewnętrznej struktury ze specyfikacją. Zdecydowanie powinniśmy, gdy nasze API jest używane przez strony trzecie, ale nie tak naprawdę, kiedy kod zostanie ostatecznie wykonany w środowisku wykonawczym.

kospiotr
źródło
0

Oto jeden przykład z MDN, który okazał się bardzo pomocny w zrozumieniu jego zastosowań.

W JavaScript mamy, async functionsktóra zwraca obiekt AsyncFunction . AsyncFunctionnie jest obiektem globalnym, ale można go pobrać za pomocą constructorwłaściwości i wykorzystać.

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

// AsyncFunction constructor
var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor

var a = new AsyncFunction('a', 
                          'b', 
                          'return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);');

a(10, 20).then(v => {
  console.log(v); // prints 30 after 4 seconds
});
Hitesh Kumar
źródło
-1

To nie jest konieczne. Jest to tylko jedna z wielu tradycyjnych rzeczy, które mistrzowie OOP robią, aby spróbować przekształcić prototypowe dziedzictwo JavaScript w klasyczne. Jedyne, co następuje

Student.prototype.constructor = Student; 

robi to, że masz teraz odniesienie do bieżącego „konstruktora”.

W odpowiedzi Wayne'a, która została oznaczona jako poprawna, możesz zrobić dokładnie to samo, co poniższy kod

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  

z kodem poniżej (wystarczy zamienić this.constructor na Person)

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new Person(this.name);
}; 

Dzięki Bogu, że dzięki klasycznemu dziedziczeniu ES6 puryści mogą korzystać z natywnych operatorów języka, takich jak class, extends i super, i nie musimy widzieć jak poprawki prototype.constructor i referencje rodziców.

Roumelis George
źródło