Warum muss der Prototypkonstruktor eingestellt werden?

294

Im Abschnitt über die Vererbung im MDN-Artikel Einführung in objektorientiertes Javascript habe ich festgestellt, dass der Prototyp.constructor festgelegt wurde:

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

Dient dies einem wichtigen Zweck? Ist es in Ordnung, es wegzulassen?

dreizehnten
quelle
23
Ich bin froh, dass Sie dies gefragt haben: Ich habe gestern dieselbe Dokumentation gelesen und war neugierig auf die Gründe für die explizite Einstellung des Konstruktors.
Wylie
6
Ich musste nur darauf hinweisen, diese Frage ist jetzt in dem Artikel verlinkt, den Sie verlinkt haben!
Marie
7
nichts ist notwendig
nothingisnecessary
1
Das subclass.prototype.constructorwird darauf hinweisen, parent_classwenn Sie nicht schreiben subclass.prototype.constructor = subclass; Das heißt, die subclass.prototype.constructor()direkte Verwendung führt zu einem unerwarteten Ergebnis.
KuanYu Chu

Antworten:

263

Es ist nicht immer notwendig, aber es hat seine Verwendung. Angenommen, wir wollten eine Kopiermethode für die Basisklasse Personerstellen. So was:

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

Was passiert nun, wenn wir ein neues erstellen Studentund es kopieren?

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

Die Kopie ist keine Instanz von Student. Dies liegt daran, dass wir (ohne explizite Überprüfungen) keine Möglichkeit hätten, eine StudentKopie aus der "Basis" -Klasse zurückzugeben. Wir können nur a zurückgeben Person. Wenn wir jedoch den Konstruktor zurückgesetzt hätten:

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

... dann funktioniert alles wie erwartet:

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => true
Wayne
quelle
34
Hinweis: Das constructorAttribut hat in JS keine besondere Bedeutung, daher können Sie es auch aufrufen bananashake. Der einzige Unterschied ist , dass der Motor initialisiert automatisch constructorauf , f.prototypewenn Sie eine Funktion deklarierenf . Es kann jedoch jederzeit überschrieben werden.
user123444555621
58
@ Pumbaa80 - erhalte ich Ihren Punkt, aber die Tatsache , dass der Motor automatisch initialisiert constructorbedeutet , dass es funktioniert in JS besondere Bedeutung hat, so ziemlich per Definition.
Wayne
13
Ich möchte nur klarstellen, dass der Grund, warum das von Ihnen angegebene Verhalten funktioniert, darin besteht, dass Sie es return new this.constructor(this.name);anstelle von verwenden return new Person(this.name);. Da this.constructores sich um die StudentFunktion handelt (weil Sie sie mit festgelegt haben Student.prototype.constructor = Student;), copyruft die StudentFunktion die Funktion auf. Ich bin mir nicht sicher, was Sie mit dem //just as badKommentar vorhatten.
CEGRD
12
@lwburk was meinst du mit "// genauso schlecht"?
CEGRD
6
Ich glaube ich verstehe. Was aber, wenn der StudentKonstruktor ein zusätzliches Argument hinzugefügt hätte wie : Student(name, id)? Müssen wir dann die copyFunktion überschreiben , die PersonVersion aus ihr heraus aufrufen und dann auch die zusätzliche idEigenschaft kopieren ?
Snapfractalpop
76

Dient dies einem wichtigen Zweck?

Ja und nein.

In ES5 und früheren Versionen wurde JavaScript selbst nicht verwendet constructor für nichts verwendet. Es wurde definiert, dass das Standardobjekt in der prototypeEigenschaft einer Funktion es haben würde und dass es auf die Funktion zurückgreifen würde, und das war es . Nichts anderes in der Spezifikation bezog sich überhaupt darauf.

Dies änderte sich in ES2015 (ES6), das damit begann, es in Bezug auf Vererbungshierarchien zu verwenden. Promise#thenVerwendet zum Beispiel dieconstructor Eigenschaft des Versprechens Sie es fordern (via SpeciesConstructor ) , wenn das neue Versprechen Gebäude zurückzukehren. Es ist auch an der Subtypisierung von Arrays beteiligt (über ArraySpeciesCreate ).

Außerhalb der Sprache selbst wurde sie manchmal verwendet, wenn versucht wurde, generische "Klon" -Funktionen zu erstellen, oder nur allgemein, wenn sie sich auf das beziehen wollten, von dem sie glaubten, dass es die Konstruktorfunktion des Objekts ist. Ich habe die Erfahrung gemacht, dass es selten ist, aber manchmal wird es benutzt.

Ist es in Ordnung, es wegzulassen?

Es ist standardmäßig vorhanden. Sie müssen es nur zurücksetzen, wenn Sie das Objekt in der Eigenschaft einer Funktion ersetzenprototype :

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

Wenn Sie dies nicht tun:

Student.prototype.constructor = Student;

... Student.prototype.constructorerbt dann von Person.prototypedem (vermutlich) hat constructor = Person. Es ist also irreführend. Und wenn Sie etwas unterordnen, das es verwendet (wie Promiseoder Array), und nicht class¹ verwenden (was dies für Sie erledigt), sollten Sie natürlich sicherstellen, dass Sie es richtig einstellen. Also im Grunde: Es ist eine gute Idee.

Es ist in Ordnung, wenn nichts in Ihrem Code (oder Bibliothekscode, den Sie verwenden) es verwendet. Ich habe immer dafür gesorgt, dass es richtig verkabelt ist.

Mit dem Schlüsselwort von ES2015 (auch bekannt als ES6) müssen classwir es natürlich die meiste Zeit nicht mehr verwenden, da es für uns dann behandelt wird

class Student extends Person {
}

¹ "... wenn Sie etwas unterordnen, das es verwendet (wie Promiseoder Array) und nicht class..."  - Das ist möglich , aber es ist ein echter Schmerz (und ein bisschen albern). Du musst benutzen Reflect.construct.

TJ Crowder
quelle
12

TLDR; Nicht besonders notwendig, wird aber wahrscheinlich auf lange Sicht helfen, und es ist genauer, dies zu tun.

HINWEIS: Viel bearbeitet, da meine vorherige Antwort verwirrend geschrieben war und einige Fehler aufwies, die ich in meiner Eile zur Beantwortung verpasst hatte. Vielen Dank an diejenigen, die auf einige ungeheure Fehler hingewiesen haben.

Grundsätzlich geht es darum, Unterklassen in Javascript korrekt zu verdrahten. Wenn wir eine Unterklasse erstellen, müssen wir einige funky Dinge tun, um sicherzustellen, dass die prototypische Delegierung ordnungsgemäß funktioniert, einschließlich des Überschreibens eines prototypeObjekts. Das Überschreiben eines prototypeObjekts umfasst dieconstructor , daher müssen wir die Referenz korrigieren.

Lassen Sie uns kurz durchgehen, wie 'Klassen' in ES5 funktionieren.

Angenommen, Sie haben eine Konstruktorfunktion und ihren 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',
}

Wenn Sie den Konstruktor zum Instanziieren aufrufen, sagen Sie Adam:

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

Das newmit 'Person' aufgerufene Schlüsselwort führt den Personenkonstruktor grundsätzlich mit einigen zusätzlichen Codezeilen aus:

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

Wenn wir console.log(adam.species), wird die Suche in der Fail - adamInstanz, und schauen Sie die prototypische Kette der jeweils .prototype, das ist Person.prototype- und Person.prototype hat eine .speciesEigenschaft, so dass die Lookup auf Erfolg haben wird Person.prototype. Es wird dann protokolliert'human' .

Hier zeigt der Person.prototype.constructorWille richtig auf Person.

Nun also der interessante Teil, die sogenannte "Unterklasse". Wenn wir eine StudentKlasse erstellen möchten, dh eine Unterklasse der PersonKlasse mit einigen zusätzlichen Änderungen, müssen wir sicherstellen, dass die Student.prototype.constructorPunkte für die Genauigkeit auf Student zeigen.

Das macht es nicht alleine. Wenn Sie eine Unterklasse erstellen, sieht der Code folgendermaßen aus:

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

Wenn Sie new Student()hier aufrufen, wird ein Objekt mit allen gewünschten Eigenschaften zurückgegeben. Hier, wenn wir überprüfen eve instanceof Person, würde es zurückkehren false. Wenn wir versuchen, darauf zuzugreifen eve.species, würde es zurückkehrenundefined .

Mit anderen Worten, wir müssen die Delegierung so verkabeln, dass eve instanceof Persontrue zurückgegeben wird und Instanzen der StudentDelegierung korrekt an Student.prototypeund dann gesendet werden Person.prototype.

ABER da wir es mit dem newSchlüsselwort aufrufen , erinnern Sie sich, was dieser Aufruf hinzufügt? Es würde heißen Object.create(Student.prototype), wie wir diese Delegationsbeziehung zwischen Studentund aufbauen Student.prototype. Beachten Sie, dass im Moment Student.prototypeleer ist. So aufzublicken .specieseine Instanz Studentfehlschlagen würde , wie es Delegierten nur Student.prototype , und die .speciesEigenschaft existiert nicht auf Student.prototype.

Wenn wir assign tun Student.prototypezu Object.create(Person.prototype), Student.prototypeselbst dann Delegierten Person.prototypeund aufzublicken eve.specieszurückkehren wird , humanwie wir es erwarten. Vermutlich möchten wir, dass es von Student.prototype AND Person.prototype erbt. Also müssen wir das alles beheben.

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

Jetzt funktioniert die Delegation, aber wir überschreiben Student.prototypemit einem von Person.prototype. Wenn wir also anrufen Student.prototype.constructor, würde es auf Personstatt zeigen Student. Dies ist , warum wir es beheben müssen.

// 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 ist unsere constructorEigenschaft eine Referenz, die sich auf eine Funktion bezieht, die wir mit der Absicht geschrieben haben, ein 'Konstruktor' zu sein. Abgesehen von dem, was dienew Schlüsselwort uns gibt, ist der Konstruktor ansonsten eine 'einfache' Funktion.

In ES6 ist das constructorjetzt in die Art und Weise integriert, wie wir Klassen schreiben - wie in wird es als Methode bereitgestellt, wenn wir eine Klasse deklarieren. Dies ist einfach syntaktischer Zucker, aber es bietet uns einige Annehmlichkeiten wie den Zugang zu einem, superwenn wir eine bestehende Klasse erweitern. Also würden wir den obigen Code so schreiben:

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
quelle
eve instanceof Studentzurückgegeben true. Erläuterungen finden Sie unter stackoverflow.com/questions/35537995/… . Auch wenn Sie sagen, which is, at the moment, nothingworauf beziehen Sie sich? Jede Funktion hat einen Prototyp. Wenn ich das überprüfe Student.prototype, ist es etwas.
Aseem Bansal
Mein Fehler. Es sollte 'eve instanceof Person' gelesen haben, was false zurückgeben würde. Ich werde diesen Teil ändern. Sie haben Recht, dass jede Funktion eine Prototyp-Eigenschaft hat. Allerdings , ohne den Prototyp zu zuweisen Object.create(Person.prototype), die Student.prototypeleer ist . Wenn wir uns also anmelden eve.species, wird es nicht ordnungsgemäß an seine Oberklasse Person delegiert, und es wird nicht protokolliert 'human'. Vermutlich wollen wir, dass jede Unterklasse von ihrem Prototyp und auch von ihrem Super-Prototyp erbt.
Mensch
Zur Verdeutlichung which is, at the moment, nothingmeinte ich, dass das Student.prototypeObjekt leer ist.
Mensch
Mehr zum Prototyp: Ohne die Zuordnung Student.prototypezu Object.create(Person.prototype)- das ist, wenn man die gleiche Art und Weise erinnert, werden alle Instanzen der Person eingerichtet Delegierten Person.prototype- eine Eigenschaft auf einer Instanz des Nachschlagen Studentwürde delegiert nur Student.prototype . Also eve.specieswird seine Suche fehlschlagen. Wenn wir es zuweisen, werden die Student.prototypeDelegierten selbst an Person.prototypeund das Nachschlagen eve.specieswird zurückkehren human.
bthehuman
Es scheint, dass hier einige Dinge falsch sind: "Es ist notwendig, wenn Sie versuchen, '[...]' Unterklassen 'zu emulieren instance, damit diese genau sind , wenn Sie überprüfen, ob eine Instanz der Konstruktor' Unterklassen 'ist." Nein, instanceofbenutzt nicht constructor. "Wenn wir jedoch den .prototype.constructor des Schülers nachschlagen, würde er immer noch auf Person verweisen." Nein, das wird es sein Student. Ich verstehe den Sinn dieses Beispiels nicht. Das Aufrufen einer Funktion in einem Konstruktor ist keine Vererbung. "In ES6 ist der Konstruktor jetzt eine tatsächliche Funktion anstelle eines Verweises auf eine Funktion." Äh was?
Felix Kling
10

Ich würde nicht zustimmen. Der Prototyp muss nicht eingestellt werden. Nehmen Sie genau denselben Code, aber entfernen Sie die Zeile prototype.constructor. Ändert sich etwas? Nehmen Sie jetzt die folgenden Änderungen vor:

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

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

und am Ende des Testcodes ...

alert(student1.favoriteColor);

Die Farbe wird blau sein.

Eine Änderung am prototype.constructor macht meiner Erfahrung nach nicht viel, es sei denn, Sie machen sehr spezifische, sehr komplizierte Dinge, die wahrscheinlich sowieso keine gute Praxis sind :)

Bearbeiten: Nachdem Sie ein wenig im Web gestöbert und experimentiert haben, sieht es so aus, als würden die Leute den Konstruktor so einstellen, dass er wie das aussieht, was mit "neu" erstellt wird. Ich denke, ich würde argumentieren, dass das Problem dabei ist, dass Javascript eine Prototypsprache ist - es gibt keine Vererbung. Die meisten Programmierer haben jedoch einen Programmierhintergrund, der die Vererbung als "Weg" bezeichnet. Wir haben uns also alle möglichen Dinge ausgedacht, um diese prototypische Sprache zu einer "klassischen" Sprache zu machen. Zum Beispiel die Erweiterung von "Klassen". In dem Beispiel, das sie gegeben haben, ist ein neuer Schüler eine Person - es geht nicht von einem anderen Schüler aus. Der Schüler dreht sich alles um die Person, und was auch immer die Person ist, der Schüler ist es auch. Erweitern Sie den Schüler und was auch immer Sie '

Crockford ist ein bisschen verrückt und übereifrig, aber lesen Sie einige der Dinge, die er geschrieben hat, ernsthaft durch. Dadurch werden Sie diese Dinge ganz anders betrachten.

Stephen
quelle
8
Dies erbt die Prototypenkette nicht.
Cypher
1
@Cypher langsames Klatschen Willkommen zum Gespräch, vier Jahre später. Ja, die Prototypenkette wird vererbt, unabhängig davon, ob Sie den Prototyp.constructor überschreiben. Probieren Sie es aus.
Stephen
7
Ihnen fehlt der Code, der den Prototyp erbt. Willkommen im Internet.
Cypher
1
Das @ Code-Code-Snippet basierte auf dem Code im verlinkten Artikel. Willkommen beim Lesen der gesamten Frage. Oh. Warten.
Stephen
1
@macher Ich meinte es als klassisches Erbe. Schlechte Wortwahl meinerseits.
Stephen
9

Dies hat die große Gefahr, dass, wenn Sie geschrieben haben

Student.prototype.constructor = Student;

aber dann, wenn es einen Lehrer gab, dessen Prototyp auch Person war und Sie geschrieben haben

Teacher.prototype.constructor = Teacher;

dann ist der Schülerkonstruktor jetzt Lehrer!

Bearbeiten: Sie können dies vermeiden, indem Sie sicherstellen, dass Sie die Schüler- und Lehrerprototypen mithilfe neuer Instanzen der mit Object.create erstellten Person-Klasse wie im Mozilla-Beispiel festgelegt haben.

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);
James D.
quelle
1
Student.prototype = Object.create(...)wird in dieser Frage angenommen. Diese Antwort fügt nichts als mögliche Verwirrung hinzu.
André Chalella
3
@ AndréNeves Ich fand diese Antwort hilfreich. Object.create(...)wird in dem MDN-Artikel verwendet, der die Frage hervorgebracht hat, jedoch nicht in der Frage selbst. Ich bin sicher, dass viele Leute nicht durchklicken.
Alex Ross
Der verlinkte Artikel, auf den in der Frage bereits verwiesen wird, verwendet Object.create (). Diese Antwort und die Bearbeitung der Antwort sind nicht wirklich relevant, und es ist gelinde gesagt verwirrend :-)
Drenai
1
Der umfassendere Punkt ist, dass es Fallstricke gibt, die Menschen, die neu in Javascript-Prototypen sind, erwischen. Wenn wir 2016 diskutieren, sollten Sie wirklich ES6-Klassen, Babel und / oder Typescript verwenden. Wenn Sie Klassen jedoch wirklich manuell auf diese Weise erstellen möchten, ist es hilfreich zu verstehen, wie Prototypketten wirklich funktionieren, um ihre Leistung zu nutzen. Sie können jedes Objekt als Prototyp verwenden und möchten möglicherweise kein separates Objekt neu erstellen. Bevor HTML 5 vollständig verbreitet war, war Object.create nicht immer verfügbar, sodass es einfacher war, eine Klasse falsch einzurichten.
James D
5

Bisher herrscht noch Verwirrung.

Folgen Sie dem ursprünglichen Beispiel, da Sie ein vorhandenes Objekt haben student1als:

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

Angenommen, Sie möchten nicht wissen, wie student1erstellt wird, Sie möchten nur ein anderes Objekt wie dieses. Sie können die Konstruktoreigenschaft von student1like verwenden:

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

Hier können die Eigenschaften Studentnicht abgerufen werden, wenn die Konstruktoreigenschaft nicht festgelegt ist. Vielmehr wird ein PersonObjekt erstellt.

Mahavir
quelle
2

Ich habe ein schönes Codebeispiel dafür erhalten, warum es wirklich notwendig ist, den Prototyp-Konstruktor festzulegen.

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 ???? 
*/
user3877965
quelle
Ihre createNewCarMethode schafft Fabriken!? Auch dies sieht so aus, als hätte es var audiFactory = new CarFactory("Audi")eher als Vererbung verwendet werden sollen.
Bergi
Ihr Beispiel wird this.constructorintern verwendet, daher ist es nicht verwunderlich, dass es festgelegt werden muss. Haben Sie ein Beispiel ohne es?
Dmitri Zaitsev
1

Heutzutage sind keine gezuckerten Funktionsklassen oder die Verwendung von "Neu" erforderlich. Verwenden Sie Objektliterale.

Der Objektprototyp ist bereits eine 'Klasse'. Wenn Sie ein Objektliteral definieren, ist es bereits eine Instanz des Prototypobjekts. Diese können auch als Prototyp eines anderen Objekts usw. fungieren.

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]

Das ist eine Lektüre wert :

Klassenbasierte objektorientierte Sprachen wie Java und C ++ basieren auf dem Konzept zweier unterschiedlicher Entitäten: Klassen und Instanzen.

...

Eine prototypbasierte Sprache wie JavaScript unterscheidet diese Unterscheidung nicht: Sie enthält lediglich Objekte. Eine prototypbasierte Sprache hat den Begriff eines prototypischen Objekts, eines Objekts, das als Vorlage verwendet wird, um die anfänglichen Eigenschaften für ein neues Objekt abzurufen. Jedes Objekt kann seine eigenen Eigenschaften angeben, entweder beim Erstellen oder zur Laufzeit. Darüber hinaus kann jedes Objekt als Prototyp für ein anderes Objekt zugeordnet werden, sodass das zweite Objekt die Eigenschaften des ersten Objekts gemeinsam nutzen kann

ucsarge
quelle
1

Es ist notwendig, wenn Sie eine Alternative zu toStringohne Monkeypatching benötigen :

//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
quelle
Was soll das alles tun? foo.constructor()??
Ry-
0

EDIT, ich habe mich tatsächlich geirrt. Das Kommentieren der Zeile ändert nichts an ihrem Verhalten. (Ich habe es getestet)


Ja, das ist notwendig. Wenn Sie das tun

Student.prototype = new Person();  

Student.prototype.constructorwird Person. Daher Student()würde ein Aufruf ein Objekt zurückgeben, das von erstellt wurde Person. Wenn du es dann tust

Student.prototype.constructor = Student; 

Student.prototype.constructorwird zurückgesetzt auf Student. Wenn Sie Student()es jetzt ausführen Student, wird der übergeordnete Konstruktor aufgerufen und Parent()das korrekt geerbte Objekt zurückgegeben. Wenn Sie Student.prototype.constructorvor dem Aufruf nicht zurückgesetzt haben, erhalten Sie ein Objekt, für das keine der Eigenschaften festgelegt ist Student().

unsichtbarer Bob
quelle
3
Die Prototypkonstruktion kann zu einer Person werden, dies ist jedoch angemessen, da alle Eigenschaften und Methoden von der Person geerbt werden. Das Erstellen eines neuen Student () ohne Festlegen des prototype.constructor ruft seinen eigenen Konstruktor entsprechend auf.
Stephen
0

Bei einfacher Konstruktorfunktion:

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(){...}

Standardmäßig (aus der Spezifikation https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ) erhalten alle Prototypen automatisch eine Eigenschaft namens Konstruktor, die auf die Funktion zurück verweist was es ist eine Eigenschaft. Abhängig vom Konstruktor können dem Prototyp andere Eigenschaften und Methoden hinzugefügt werden, was nicht sehr üblich ist, aber dennoch für Erweiterungen zulässig ist.

Also einfach antworten: Wir müssen sicherstellen, dass der Wert in prototype.constructor korrekt eingestellt ist, wie es in der Spezifikation angenommen wird.

Müssen wir diesen Wert immer richtig einstellen? Es hilft beim Debuggen und macht die interne Struktur gegenüber der Spezifikation konsistent. Wir sollten auf jeden Fall, wenn unsere API von den Drittanbietern verwendet wird, aber nicht wirklich, wenn der Code schließlich zur Laufzeit ausgeführt wird.

kospiotr
quelle
0

Hier ist ein Beispiel von MDN, das ich sehr hilfreich fand, um seine Verwendung zu verstehen.

In JavaScript gibt es async functionsdas AsyncFunction- Objekt. AsyncFunctionist kein globales Objekt, aber man kann es mithilfe der constructorEigenschaft abrufen und verwenden.

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
quelle
-1

Es ist nicht erforderlich. Es ist nur eines der vielen Dinge, die traditionelle OOP-Champions tun, um zu versuchen, die prototypische Vererbung von JavaScript in eine klassische Vererbung umzuwandeln. Das einzige was das folgende ist

Student.prototype.constructor = Student; 

tut, ist, dass Sie jetzt eine Referenz des aktuellen "Konstruktors" haben.

In Waynes Antwort, die als richtig markiert wurde, könnten Sie genau dasselbe tun wie im folgenden Code

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

mit dem folgenden Code (ersetzen Sie einfach this.constructor durch Person)

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

Gott sei Dank können Puristen mit klassischer Vererbung von ES6 die nativen Operatoren der Sprache wie class, extensiv und super verwenden, und wir müssen nicht wie prototype.constructor-Korrekturen und übergeordnete Referenzen sehen.

Roumelis George
quelle