Ich bin neu bei JavaScript OOP. Können Sie bitte den Unterschied zwischen den folgenden Codeblöcken erklären? Ich habe getestet und beide Blöcke funktionieren. Was ist die beste Praxis und warum?
Erster Block:
function Car(name){
this.Name = name;
}
Car.prototype.Drive = function(){
console.log("My name is " + this.Name + " and I'm driving.");
}
SuperCar.prototype = new Car();
SuperCar.prototype.constructor = SuperCar;
function SuperCar(name){
Car.call(this, name);
}
SuperCar.prototype.Fly = function(){
console.log("My name is " + this.Name + " and I'm flying!");
}
var myCar = new Car("Car");
myCar.Drive();
var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();
Zweiter Block:
function Car(name){
this.Name = name;
this.Drive = function(){
console.log("My name is " + this.Name + " and I'm driving.");
}
}
SuperCar.prototype = new Car();
function SuperCar(name){
Car.call(this, name);
this.Fly = function(){
console.log("My name is " + this.Name + " and I'm flying!");
}
}
var myCar = new Car("Car");
myCar.Drive();
var mySuperCar = new SuperCar("SuperCar");
mySuperCar.Drive();
mySuperCar.Fly();
Warum hat der Autor die Drive
- und Fly
-Methoden mit prototype
hinzugefügt und sie nicht als this.Drive
-Methode in der Car
-Klasse und als this.Fly
in der SuperCar
-Klasse deklariert?
Warum muss SuperCar.prototype.constructor
auf SuperCar
zurückgesetzt werden? Wird die constructor
-Eigenschaft überschrieben, wenn prototype
eingestellt ist? Ich habe diese Zeile auskommentiert und nichts geändert.
Warum rufen Sie Car.call(this, name);
im Konstruktor SuperCar
auf? Werden Eigenschaften und Methoden von Car
nicht "vererbt", wenn ich dies tue
var myCar = new Car("Car");
Die beiden Blöcke unterscheiden sich dahingehend, dass im ersten Beispiel Drive()
nur einmal vorhanden ist, während bei der zweiten Annäherung Drive()
pro Instanz vorhanden ist (jedes Mal, wenn Sie new Car()
die Funktion drive()
wird erneut erstellt). Oder anders gesagt, der erste verwendet den Prototyp zum Speichern der Funktion und der zweite den Konstruktor. Die Suche nach Funktionen ist Konstruktor und dann Prototyp. Für Ihre Suche nach Drive()
findet sie es unabhängig davon, ob es sich im Konstruktor oder im Prototyp befindet. Die Verwendung des Prototyps ist effizienter, da Sie normalerweise nur einmal pro Typ eine Funktion benötigen.
Der Aufruf new
in Javascript setzt den Konstruktor im Prototyp automatisch. Wenn Sie den Prototyp überschreiben, müssen Sie den Konstruktor manuell einstellen.
Vererbung in Javascript hat nichts wie super
. Wenn Sie also eine Unterklasse haben, besteht die einzige Möglichkeit, den Superkonstruktor aufzurufen, beim Namen.
Zum Hinzufügen von der Antwort von Norbert Hartl ist SuperCar.prototype.constructor nicht erforderlich, aber einige verwenden es als bequeme Möglichkeit, die Konstruktionsfunktion eines Objekts (in diesem Fall SuperCar-Objekte) zu erhalten.
Nur aus dem ersten Beispiel ist Car.call (dieser Name) in der SuperCar-Konstruktorfunktion, denn wenn Sie Folgendes tun:
var mySuperCar = new SuperCar("SuperCar");
Dies ist, was JavaScript tut:
Beachten Sie, dass JavaScript für Sie kein Auto genannt hat. Prototypen wie sie sind, jede Eigenschaft oder Methode, die Sie nicht selbst für SuperCar festlegen, wird in Car nachgeschlagen. Manchmal ist das gut, z. SuperCar verfügt nicht über eine Drive-Methode, kann jedoch die Car-Methode gemeinsam nutzen, sodass alle SuperCars die gleiche Drive-Methode verwenden. Andere Zeiten, in denen Sie nicht teilen möchten, wie jeder SuperCar seinen eigenen Namen hat. Wie kann man also den Namen jedes SuperCars auf sein eigenes Ding festlegen? Sie können this.Name in der SuperCar-Konstruktorfunktion festlegen:
function SuperCar(name){
this.Name = name;
}
Das funktioniert, aber warten Sie eine Sekunde. Haben wir im Autobauer nicht genau dasselbe gemacht? Ich will uns nicht wiederholen. Da Car den Namen bereits festlegt, nennen wir es einfach.
function SuperCar(name){
this = Car(name);
}
Hoppla, Sie wollen niemals die spezielle this
-Objektreferenz ändern. Erinnern Sie sich an die 4 Schritte? Hängen Sie an dem Objekt, das Sie von JavaScript erhalten haben, denn nur so können Sie die kostbare interne Prototypverbindung zwischen Ihrem SuperCar-Objekt und Car beibehalten. Wie setzen wir also Namen, ohne uns zu wiederholen und unser neues SuperCar-Objekt nicht wegzuwerfen, weil JavaScript so viel Aufwand für die Vorbereitung auf uns aufgewendet hat?
Zwei Dinge. Erstens: Die Bedeutung von this
ist flexibel. Zweitens: Auto ist eine Funktion. Es ist möglich, Car nicht mit einem makellosen, frisch instanziierten Objekt zu rufen, sondern mit einem SuperCar-Objekt. Das gibt uns die endgültige Lösung, die Teil des ersten Beispiels in Ihrer Frage ist:
function SuperCar(name){
Car.call(this, name);
}
Als Funktion kann Car mit der call-Methode der Funktion aufgerufen werden, wodurch die Bedeutung von this
in Car in die SuperCar-Instanz geändert wird, die wir gerade aufbauen. Presto! Jetzt erhält jeder SuperCar eine eigene Name-Eigenschaft.
Zusammenfassend gibt Car.call(this, name)
im SuperCar-Konstruktor jedem neuen SuperCar-Objekt seine eigene eindeutige Name-Eigenschaft, jedoch ohne den Code zu duplizieren, der bereits in Car vorhanden ist.
Prototypen sind nicht unheimlich, wenn Sie sie verstanden haben, aber sie ähneln dem klassischen Klassen-/Vererbungsmodell OOP überhaupt nicht. Ich habe einen Artikel über das Prototypenkonzept in JavaScript geschrieben. Es wurde für eine Spiel-Engine geschrieben, die JavaScript verwendet, aber es ist dieselbe JavaScript-Engine, die auch von Firefox verwendet wird. Daher sollte alles relevant sein. Hoffe das hilft.
Norbert, Sie sollten beachten, dass Ihr erstes Beispiel ziemlich genau das ist, was Douglas Crockford als pseudoklassische Vererbung bezeichnet. Etwas zu beachten:
Zum Schluss möchte ich noch erwähnen, dass ich mehrere Beispiele für TDD-JavaScript-Vererbungscode habe, der hier funktioniert: TDD-JavaScript-Vererbungscode und Essay Ich würde gerne Ihr Feedback erhalten, da ich es verbessern und behalten möchte es ist Open Source. Das Ziel ist es, klassischen Programmierern zu helfen, sich schnell mit JavaScript vertraut zu machen und die beiden Bücher Crockford und Zakas zu ergänzen.
function abc() {
}
Prototypmethoden und Eigenschaften, die für die Funktion abc erstellt wurden
abc.prototype.testProperty = 'Hi, I am prototype property';
abc.prototype.testMethod = function() {
alert('Hi i am prototype method')
}
Neue Instanzen für die Funktion abc anlegen
var objx = new abc();
console.log(objx.testProperty); // will display Hi, I am prototype property
objx.testMethod();// alert Hi i am prototype method
var objy = new abc();
console.log(objy.testProperty); //will display Hi, I am prototype property
objy.testProperty = Hi, I am over-ridden prototype property
console.log(objy.testProperty); //will display Hi, I am over-ridden prototype property
http://astutejs.blogspot.in/2015/10/javascript-prototype-is-easy.html
Ich bin nicht zu 100% sicher, aber ich glaube, der Unterschied besteht darin, dass das zweite Beispiel einfach den Inhalt der Car-Klasse in das SuperCar-Objekt kopiert, während das erste den SuperCar-Prototyp mit der Car-Klasse verknüpft, sodass sich die Laufzeit in der ändert Die Fahrzeugklasse betrifft auch die SuperCar-Klasse.
Hier gibt es mehrere Fragen:
Können Sie bitte den Unterschied zwischen den folgenden Codeblöcken erklären. Ich habe getestet und beide Blöcke funktionieren.
Die erste erstellt nur eine Drive
-Funktion, die zweite erstellt zwei davon: eine für myCar
und eine für mySuperCar
.
Hier ist Code, der beim Ausführen des ersten oder zweiten Blocks unterschiedliche Ergebnisse liefert:
myCar.Fly === mySuperCar.Fly // true only in the first case
Object.keys(myCar).includes("Fly") // true only in the second case
Object.keys(Car.prototype).length === 0 // true only in the second case
Was ist die beste Vorgehensweise und warum?
Warum hat der AutorDrive
undFly
Methoden mitprototype
hinzugefügt, aber nicht alsthis.Drive
Methode deklariert inCar
Klasse undthis.Fly
inSuperCar
Klasse?
Es ist besser, Methoden am Prototyp zu definieren, weil:
Object.create(Car.prototype)
aufgerufen wird).Warum muss
SuperCar.prototype.constructor
aufSuperCar
zurückgesetzt werden? Wird die Eigenschaftconstructor
überschrieben, wennprototype
festgelegt wird? Ich habe diese Zeile auskommentiert und nichts geändert.
Die Eigenschaft constructor
wird nicht überschrieben, wenn prototype
festgelegt ist. Aber der Konstruktor von new Car()
ist Car
. Wenn Sie also new Car()
auf SuperCar.prototype
setzen, ist SuperCar.prototype.constructor
offensichtlich Car
.
Solange Sie prototype
nicht neu zuweisen, gibt es eine Invarianz: Constructor.prototype.constructor === Constructor
. Dies gilt beispielsweise für Car
: Car.prototype.constructor === Car
, aber ebenso für Array
, Object
, String
]_, ...usw.
Wenn Sie jedoch prototype
ein anderes Objekt zuweisen, wird diese Invarianz aufgehoben. Normalerweise ist dies kein Problem (wie Sie bemerkt haben), aber es ist besser, es wieder herzustellen, da es die Frage beantwortet "Welcher Konstruktor verwendet dieses Prototypobjekt beim Erstellen neuer Instanzen?" Etwas Code kann eine solche Inspektion tun und davon abhängen. Siehe "Warum muss der Prototyp-Konstruktor festgelegt werden?" für solche Fälle.
Warum im Konstruktor
SuperCar
Car.call(this, name);
aufrufen? Werden Eigenschaften und Methoden von Car nicht "geerbt", wenn ich das tue?var myCar = new Car("Car");
Wenn Sie Car.call(this, name);
nicht ausführen, hat Ihre SuperCar
-Instanz keine Namenseigenschaft. Sie können sich natürlich dafür entscheiden, stattdessen nur this.name = name;
auszuführen, wodurch nur der Code kopiert wird, der sich im Konstruktor Car
befindet. In komplexeren Situationen wäre es jedoch eine schlechte Praxis, eine solche Codeduplizierung durchzuführen .
Es wäre nicht hilfreich, new Car(name)
im Konstruktor SuperCar
aufzurufen, da dadurch ein anderes Objekt erstellt wird, während Sie das this
Objekt. Wenn Sie new
nicht verwenden (stattdessen call
verwenden), weisen Sie die Funktion Car
tatsächlich an, nicht als Konstruktor auszuführen (dh not ein neues Objekt erstellen), aber um das Objekt zu verwenden, übergeben Sie es stattdessen.
In modernen Versionen von JavaScript können Sie super(name)
anstelle von Car.call(this, name)
verwenden:
function SuperCar(name) {
super(name);
}
Heute würden Sie auch die Syntax class
verwenden und den ersten Codeblock der Frage wie folgt schreiben:
class Car {
constructor(name) {
this.name = name;
}
drive() {
console.log(`My name is ${this.name} and I'm driving.`);
}
}
class SuperCar extends Car {
constructor(name) {
super(name);
}
fly() {
console.log(`My name is ${this.name} and I'm flying!`);
}
}
const myCar = new Car("Car");
myCar.drive();
const mySuperCar = new SuperCar("SuperCar");
mySuperCar.drive();
mySuperCar.fly();
Beachten Sie, dass Sie die Eigenschaft prototype
nicht einmal erwähnen müssen, um das Ziel zu erreichen. Die class ... extends
-Syntax sorgt auch dafür, dass die prototype.constructor
-Eigenschaft als erster Block in Ihrer Frage festgelegt wird.