2017-03-28 6 views
1

Ich habe den folgenden Code-Schnipsel, die die Antwort auf diese Frage stellt:Javascript Prototyp Konstruktor Erklärung

Sie sind eine Klasse namens Berater erstellen, die von der Klasse Employee MustInherit. Die Consultant-Klasse muss die geerbte PayEmployee-Methode ändern. Zukünftige Instanzen von Consultant müssen mit der überschriebenen Methode erstellt werden.

function Employee() {} 

Employee.prototype.PayEmployee = function (){ 
    alert(‘Hi there!’); 
} 

function Consultant() { 
    Employee.call(this);       // X 
} 
Consultant.prototype = new Employee(); 
Consultant.prototype.constructor = Consultant; // X 

Consultant.prototype.PayEmployee = function() 
{ 
    alert(‘Pay Consultant’); 
} 

Ich kann wirklich meinen Kopf nicht darum wickeln. Warum sind mit "X" markierte Zeilen notwendig? Könnte ich nicht einfach den Code ohne diese Zeilen schreiben und es wäre genau das gleiche? Nach meinen Tests würde es keinen Unterschied machen:

Kann mir jemand erklären, was der Unterschied zwischen diesen beiden Codes ist, und was, warum ich einen über den anderen verwenden sollten ?

Vielen Dank im Voraus

+0

* zur Zeit * können sie keinen Unterschied anzeigen. Versuchen Sie, andere Eigenschaften und Methoden für die Basisklasse 'Employee' zu ​​definieren, aber nicht' Consultant'. Ich glaube, Sie werden etwas finden. – Jokester

Antwort

3

Könnte ich nicht schreiben Sie einfach den Code ohne diese Linien und es würde genau das gleiche?

Nein, Sie brauchen beide im allgemeinen Fall. Ja, in bestimmten Fällen können Sie ohne eines oder beide davonkommen.

Zunächst einmal, beachten Sie, dass dieser Code einen allgemeinen Fehler enthält. Das ist falsch:

Consultant.prototype = new Employee(); // INCORRECT 

Es sollte sein:

Consultant.prototype = Object.create(Employee.prototype); 

new Employee hat zwei verschiedene Dinge:

  1. Es schafft ein Objekt, das Employee.prototype als Prototyp verwendet

  2. Es führt den Code in Employee, deren Aufgabe es ist, eine Instanz von Employee durch die Einrichtung von Eigenschaften und solche auf this

Aber wir wollen nicht, dass die zweiten Teil noch zu initialisieren. Consultant.prototype ist kein EmployeeInstanz, es ist ein Objekt, das der Prototyp eines Objekts über new Consultant erstellt werden wird (die eine Instanz von Employee sein wird und auch von Consultant). (Dies ist, wo JavaScript Konstruktor Funktionen und prototype Eigenschaft von Standard prototypische Vererbung abweichen. Sie können in JavaScript Standard prototypische Vererbung tun, aber wenn Sie tun, Sie nicht verwenden Konstruktorfunktionen, das prototype Eigentum oder new.)

Object.create macht nur die erste dieser beiden Aufgaben: Erzeugt ein Objekt mit Employee.prototype als seinen Prototyp.Also haben wir am Ende ein Objekt auf Consultant.prototype, das Employee.prototype als seinen Prototyp verwendet, aber ohne, das Employee aufruft und es seinen pro-Instanzinitialisierungscode ausführen lässt. (Wir machen das später, in der zweiten Zeile, die Sie mit einem X markiert haben.)

Aber warum wollen wir nicht Employee seine pro-Instanz-Initialisierung tun? Da Consultant.prototype wiederum keine Employee Instanz ist, macht es keinen Sinn, sie als eine zu initialisieren. Zum einen: Was ist, wenn Employee Parameter benötigt, um das von ihm zurückgegebene Objekt zu initialisieren? Was würden wir bei der Konstruktion von Consultant.prototype passieren? Wir haben keine instanzspezifischen Informationen, um sie zu diesem Zeitpunkt zu übergeben.

Also statt, wir tun # 1 nur oben (über Object.create), und lassen Sie # 2 (Aufruf Employee), bis wir eine Instanz sind die Konstruktion, durch Employee.call(this) von Consultant (mehr unten) aufrufen.


Um die markierten Linien:

Employee.call(this); 

Diese Linie ist wichtig Employee seine Chance zu geben, seine Initialisierung der Instanz zu tun geschaffen. Ohne es Employee hatte keine Chance, seine Arbeit zu tun: Initialisieren Sie die Employee Teile der Instanz. Zum Beispiel: Angenommen Employee wie folgt aussieht:

function Employee() { 
    this.paySchedule = "biweekly"; 
} 

Ohne die Employee.call(this); Linie in Consultant, würde diese Eigenschaft von einer Instanz über new Consultant erstellt fehlen.

Ihre zweite markierte Zeile:

Consultant.prototype.constructor = Consultant; 

Wir müssen dies tun, weil Consultant.prototype ‚s constructor Eigenschaft Consultant zeigen sollte, aber wenn wir dies nicht tun, wird es zu Employee zeigen.

Es war nicht wichtig zu viel, weil obwohl constructor in der Spezifikation definiert wurde auf diese Weise standardmäßig eingerichtet, nichts in der Spezifikation verwendet, um es zu verwenden. Das änderte sich in ES2015 (aka "ES6"): Jetzt wird die constructor Eigenschaft manchmal verwendet (zum Beispiel in Versprechungen).


Hier ist ein Beispiel, die Situation darstellt, in dem Employee Parameter.Ich habe auch PayEmployee-payEmployee geschaltet mit der überwältigenden Konvention in JavaScript sein zu halten, die nur Konstruktorfunktionen zunächst bedeckten:

function Employee(name, title) { 
 
    this.name = name; 
 
    this.title = title; 
 
} 
 

 
Employee.prototype.payEmployee = function() { 
 
    console.log("Time to pay " + this.name + " (" + this.title + ")"); 
 
}; 
 

 
function Consultant(name) { 
 
    Employee.call(this, name, "Consultant"); 
 
} 
 
Consultant.prototype = Object.create(Employee.prototype); 
 
Consultant.prototype.constructor = Consultant; 
 

 
Consultant.prototype.payEmployee = function() { 
 
    console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); 
 
}; 
 

 
var e = new Employee("Joe Bloggs", "Engineer"); 
 
e.payEmployee(); 
 

 
var c = new Consultant("John Smith"); 
 
c.payEmployee();


Und schließlich wäre ich remiss, wenn ich nicht darauf hingewiesen habe, dass ab ES2015, haben wir class Syntax dafür, die bei Bedarf für ältere Umgebungen transpiliert werden kann:

class Employee { 
 
    constructor(name, title) { 
 
     this.name = name; 
 
     this.title = title; 
 
    } 
 
    payEmployee() { 
 
     console.log("Time to pay " + this.name + " (" + this.title + ")"); 
 
    } 
 
} 
 

 
class Consultant extends Employee { 
 
    constructor(name) { 
 
     super(name, "Consultant"); 
 
    } 
 
    payEmployee() { 
 
     console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); 
 
    } 
 
} 
 

 
const e = new Employee("Joe Bloggs", "Engineer"); 
 
e.payEmployee(); 
 

 
const c = new Consultant("John Smith"); 
 
c.payEmployee();


Sie werden gefragt, ob Consultant.prottype = new Employee(); ist immer falsch. Lassen Sie sich eine Version des Codes nimmt mit new Employee für die, und das Entfernen der zwei Linien, die Sie mit X markiert, und fügen Sie eine Funktion, um Mitarbeiter, die wie es scheint, sollte zu funktionieren, aber endet für alle Arten von Verwirrung bis führende:

function Employee() { 
 
    this.staff = []; 
 
} 
 

 
Employee.prototype.payEmployee = function() { 
 
    console.log("Paying employee"); 
 
}; 
 

 
function Consultant(name) { 
 
} 
 
Consultant.prototype = new Employee(); 
 

 
Consultant.prototype.payEmployee = function() { 
 
    console.log("Time to pay " + this.name + " (" + this.title + ") -- remember, you're paying the gross, consultants handle their own tax."); 
 
}; 
 

 
var jill1 = new Employee(); 
 
jill1.staff.push("Bob"); 
 
jill1.staff.push("Jane"); 
 

 
var john1 = new Employee(); 
 
john1.staff.push("Russell"); 
 
john1.staff.push("Mohammed"); 
 

 
console.log("Jill's staff (1): " + jill1.staff.join(", ")); 
 
console.log("John's staff (1): " + john1.staff.join(", ")); 
 

 
// so far so good, but what if we use Consultant instead? 
 

 
var jill2 = new Consultant(); 
 
jill2.staff.push("Bob"); 
 
jill2.staff.push("Jane"); 
 

 
var john2 = new Consultant(); 
 
john2.staff.push("Russell"); 
 
john2.staff.push("Mohammed"); 
 

 
console.log("Jill's staff (2): " + jill2.staff.join(", ")); 
 
console.log("John's staff (2): " + john2.staff.join(", ")); 
 

 
// ??? how is it they both have all four staff?!

das Problem ist, dass die staff Array auf Consultant.prototype ist, nicht jede Instanz, und so unabhängig davon, welche Instanz die wir verwenden, sind wir immer das gleiche Array zugreifen.

Deshalb haben wir Funktionen nicht Instanzen initialisieren verwenden soll (zum Beispiel Konstruktorfunktionen) prototype Eigenschaft des Objekts für eine Konstruktorfunktion der initialisieren.

Ich sollte beachten, dass es bei der standardmäßigen prototypischen Vererbung üblich und normal wäre, eine Instanz tatsächlich als Prototyp einer anderen Instanz zu erstellen. Aber die Konstruktorfunktionen von JavaScript und ihre -Eigenschaft sind keine standardmäßige prototypische Vererbung, sie sind eine Mischung aus klassenbasierter OOP und prototypischer OOP. Sie können reine prototypische OOP in JavaScript ausführen (und viele tun dies), aber wenn Sie dies tun, verwenden Sie keine Konstruktorfunktionen, ihre prototype-Eigenschaft oder new. Sie verwenden Fabrikfunktionen und Object.create (normalerweise).

+0

Vielen Dank für Ihre großartige Erklärung! Dank dir verstehe ich jetzt die Logik dahinter. Was ich ruhig verstehen nicht, ist, wenn diese Zeile 'Consultant.prototype = new Employee();' ist wirklich immer falsch, oder wenn dies in einer Situation wie die Frage Code verwendbar ist, wo es keine Parameter. Wenn ich diese Zeile behalte und sie nicht durch die vorgeschlagene Zeile ersetze, kann ich immer noch auf den 'this.paySchedule' zugreifen, den Sie in Ihrer Antwort erwähnt haben. Warum das? Danke für Ihre Erklärung! –

+1

@ T.Bob: Wenn "Employee" keine Parameter akzeptiert oder irgendetwas anderes tut, wo es davon ausgeht, dass es seine Hauptaufgabe erledigt (Einrichten einer * Instanz *), dann mechanisch mit 'neuen Angestellten' Funktionen. Aber es ist nicht korrekt, und es ist für "Mitarbeiter" einfach, zu beginnen, nichts instanzspezifisch zu machen, sondern später bearbeitet zu werden, um instanzspezifische Arbeit zu tun. Also am besten, es von Anfang an richtig zu machen. –

+1

@ T.Bob: Re 'paySchedule': Wenn Sie' new Employee' verwenden, um 'Constructor' zu konfigurieren.prototype ', am Ende steht' paySchedule' auf 'Constructor.prototype' - und Sie sehen es auf allen 'Constructor'-Instanzen. Wenn Sie auch 'Employee.call (this)' in 'Constructor' haben, dann fügt' Employee' der Instanz ebenfalls 'paySchedule' hinzu. Ich füge ein Beispiel dafür hinzu, was passiert, wenn Sie 'new Employee' anstelle von' Object.create (Employee.prototype) '* verwenden und * Employee.call (this)' auslassen und wie es "All Go Very Wrong" gehen kann. :-) –