2010-11-11 7 views
73

Vor kurzem las ich über Call-Nutzung JavaScript in MDCJavascript Vererbung: Super-Konstruktor aufrufen oder Prototypkette verwenden?

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

eine linke von dem Beispiel unten gezeigt, verstehe ich immer noch nicht.

Warum sie Erbschaft hier wie dies

Prod_dept.prototype = new Product(); 

ist dies notwendig verwendet werden? Da es ein Aufruf an das Super-Konstruktor in

Prod_dept() 

ohnehin ist, wie dies

Product.call 

ist dies nur aus gemeinsamem Verhalten? Wann ist es besser, den Aufruf für den Superkonstruktor zu verwenden oder die Prototypkette zu verwenden?

function Product(name, value){ 
    this.name = name; 
    if(value >= 1000) 
    this.value = 999; 
    else 
    this.value = value; 
} 

function Prod_dept(name, value, dept){ 
    this.dept = dept; 
    Product.call(this, name, value); 
} 

Prod_dept.prototype = new Product(); 

// since 5 is less than 1000, value is set 
cheese = new Prod_dept("feta", 5, "food"); 

// since 5000 is above 1000, value will be 999 
car = new Prod_dept("honda", 5000, "auto"); 

Vielen Dank für die Dinge klarer

+0

Die Art, wie Sie es verwendet haben, ist fast richtig, aber Sie könnten Object.create() verwenden, anstatt die Basis mit dem neuen Schlüsselwort zu instanziieren (könnte Probleme verursachen, wenn der Basiskonstruktor Argumente benötigt). Ich habe Details in meinem Blog: http://ncombo.wordpress.com/2013/07/11/javascript-inheritance-done-right/ – Jon

+1

Beachten Sie auch, dass Product() effektiv zweimal aufgerufen wird. –

Antwort

103

Die Antwort auf die eigentliche Frage ist, dass man beides tun müssen:

  • den Prototyp auf eine Instanz des übergeordneten Einstellung initialisiert die Prototypkette (Vererbung), dies nur einmal durchgeführt wird (da der Prototyp Objekt ist geteilt).
  • Durch den Aufruf des Konstruktors der Eltern wird das Objekt selbst initialisiert, dies geschieht bei jeder Instanziierung (Sie können bei jeder Konstruktion verschiedene Parameter übergeben).

Daher sollten Sie nicht den übergeordneten Konstruktor aufrufen, wenn Sie die Vererbung einrichten. Nur beim Instanziieren eines Objekts, das von einem anderen Objekt erbt.

Chris Morgans Antwort ist fast vollständig, es fehlt ein kleines Detail (Konstruktoreigenschaft). Lassen Sie mich eine Methode zum Einrichten der Vererbung vorschlagen.

function extend(base, sub) { 
    // Avoid instantiating the base class just to setup inheritance 
    // Also, do a recursive merge of two prototypes, so we don't overwrite 
    // the existing prototype, but still maintain the inheritance chain 
    // Thanks to @ccnokes 
    var origProto = sub.prototype; 
    sub.prototype = Object.create(base.prototype); 
    for (var key in origProto) { 
    sub.prototype[key] = origProto[key]; 
    } 
    // The constructor property was set wrong, let's fix it 
    Object.defineProperty(sub.prototype, 'constructor', { 
    enumerable: false, 
    value: sub 
    }); 
} 

// Let's try this 
function Animal(name) { 
    this.name = name; 
} 

Animal.prototype = { 
    sayMyName: function() { 
    console.log(this.getWordsToSay() + " " + this.name); 
    }, 
    getWordsToSay: function() { 
    // Abstract 
    } 
} 

function Dog(name) { 
    // Call the parent's constructor 
    Animal.call(this, name); 
} 

Dog.prototype = { 
    getWordsToSay: function(){ 
     return "Ruff Ruff"; 
    } 
}  

// Setup the prototype chain the right way 
extend(Animal, Dog); 

// Here is where the Dog (and Animal) constructors are called 
var dog = new Dog("Lassie"); 
dog.sayMyName(); // Outputs Ruff Ruff Lassie 
console.log(dog instanceof Animal); // true 
console.log(dog.constructor); // Dog 

Siehe meine Blogpost für noch weitere syntaktische Zucker beim Erstellen von Klassen.http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Technik kopiert von Ext-JS und http://www.uselesspickles.com/class_library/ und einem Kommentar von https://stackoverflow.com/users/1397311/ccnokes

+6

In EcmaScript5 + (alle modernen Browser) können Sie es nicht aufzählbar machen, wenn Sie es wie folgt definieren: 'Object.defineProperty (sub.protoype, 'constructor', {enumerable: false, value: sub});' Dies So erhalten Sie genau das gleiche "Verhalten" wie wenn JavaScript eine neue Instanz einer Funktion erstellt (der Konstruktor wird automatisch als enumerable = false gesetzt) ​​ – Adaptabi

+2

Konnte man die extend-Methode nicht einfach auf zwei Zeilen vereinfachen? Nämlich: sub.prototype = Object.create (base.prototype); sub.prototype.constructor = sub; – Appetere

+0

@Steve Ja, du kannst, als ich das zum ersten Mal schrieb, 'Object.create' wurde nicht so gut unterstützt ... es zu aktualisieren. Beachten Sie, dass die meisten 'Object.create' Polyfills mit der Technik implementiert werden, die ich ursprünglich gezeigt habe. –

28

den idealen Weg, so dass es zu tun ist, um nichtProd_dept.prototype = new Product(); tun, da dies den Product Konstruktor aufruft. So ist der ideale Weg ist, es zu klonen, außer für den Konstruktor, so etwas wie dieses:

function Product(...) { 
    ... 
} 
var tmp = function(){}; 
tmp.prototype = Product.prototype; 

function Prod_dept(...) { 
    Product.call(this, ...); 
} 
Prod_dept.prototype = new tmp(); 
Prod_dept.prototype.constructor = Prod_dept; 

Dann wird der Super-Konstruktor wird bei Bauzeiten genannt, das ist, was Sie wollen, denn dann können Sie die Parameter übergeben, auch.

Wenn Sie Dinge wie die Google Closure-Bibliothek betrachten, sehen Sie, dass sie so vorgehen.

+0

Ich nenne diesen Konstruktor, der zum Einrichten der Vererbung verwendet wird, den Ersatzkonstruktor. In Ihrem Beispiel wird vergessen, die Konstruktoreigenschaft nach dem Einrichten der Vererbung zurückzusetzen, so dass Sie den Konstruktor –

+1

@Juan: OK korrekt erkennen können, um 'Prod_dept.prototype.constructor = Prod_dept;' hinzuzufügen. –

+0

@ChrisMorgan Ich habe Probleme, die letzte Zeile in Ihrem Beispiel zu verstehen: 'Prod_dept.prototype.constructor = Prod_dept;'. Zuallererst, warum wird es benötigt und warum zeigt es auf "Prod_dept" anstelle von "Product"? –

6

Wenn Sie Objektorientierte Programmierung in JavaScript getan haben, werden Sie wissen, dass Sie eine Klasse erstellen können wie folgt:

Person = function(id, name, age){ 
    this.id = id; 
    this.name = name; 
    this.age = age; 
    alert('A new person has been accepted'); 
} 

Bisher hat unsere Klassenperson nur zwei Eigenschaften und wir werden ihr einige Methoden geben. Eine saubere Methode, dies zu tun, ist , um sein 'Prototyp'-Objekt zu verwenden. Ausgehend von JavaScript 1.1 wurde das Prototyp-Objekt in JavaScript eingeführt. Dies ist ein eingebautes Objekt, das den Prozess des Hinzufügens von benutzerdefinierten Eigenschaften und Methoden zu allen Instanzen eines Objekts vereinfacht. sie fügen Sie 2 Methoden zu unserem Klassenobjekt seines ‚Prototyps‘ wie folgt verwendet:

Person.prototype = { 
    /** wake person up */ 
    wake_up: function() { 
     alert('I am awake'); 
    }, 

    /** retrieve person's age */ 
    get_age: function() { 
     return this.age; 
    } 
} 

Jetzt haben wir unsere Klasse Person definiert haben. Was wäre, wenn wir eine andere Klasse namens Manager definieren möchten, die einige Eigenschaften von Person erbt. Es gibt keinen Grund, all diese Eigenschaften erneut zu definieren, wenn wir unsere Manager-Klasse definieren, wir können sie einfach so festlegen, dass sie von der Klasse Person erben. kein JavaScript in Vererbung gebaut haben, aber wir eine Technik verwenden, können die Vererbung zu implementieren, wie folgt:

Inheritance_Manager = {}; // Wir schaffen eine Erbschaft Manager-Klasse (der Name ist beliebig)

Nun wollen wir unser Erbe Klasse geben eine Methode namens extend, die die Argumente baseClass und subClassas verwendet. Innerhalb der extend-Methode erstellen wir eine innere Klasse namens vererbung function vererbung() {}. Der Grund, warum wir diese innere -Klasse verwenden, besteht darin, Verwechslungen zwischen den baseClass- und subClass-Prototypen zu vermeiden. Als nächstes stellen wir den Prototyp unserer Vererbungsklasse auf den baseClass-Prototyp wie mit dem folgenden Code: vererbung.prototype = baseClass. Prototyp; Dann kopieren wir den Vererbungs-Prototyp wie folgt in den Unterklassen-Prototyp: subClass.prototype = new vererbung(); Als nächstes müssen Sie den Konstruktor für unsere Unterklasse wie folgt angeben: subClass.prototype.constructor = subClass; Sobald wir mit unserem SubClass-Prototyping fertig sind, können wir die nächsten zwei Codezeilen festlegen, um einige Basisklassenzeiger zu setzen.

subClass.baseConstructor = baseClass; 
subClass.superClass = baseClass.prototype; 

Hier ist der vollständige Code für unsere verlängern Funktion:

Inheritance_Manager.extend = function(subClass, baseClass) { 
    function inheritance() { } 
    inheritance.prototype = baseClass.prototype; 
    subClass.prototype = new inheritance(); 
    subClass.prototype.constructor = subClass; 
    subClass.baseConstructor = baseClass; 
    subClass.superClass = baseClass.prototype; 
} 

Jetzt, wo wir unser Erbe umgesetzt haben, können wir es benutzen unsere Klassen zu erweitern. In diesem Fall sind wir zu werden unsere Klasse Person in eine Manager-Klasse erweitern, wie folgt:

Wir definieren die Klasse Manager

Manager = function(id, name, age, salary) { 
    Person.baseConstructor.call(this, id, name, age); 
    this.salary = salary; 
    alert('A manager has been registered.'); 
} 

wir machen es Form Person

Inheritance_Manager.extend(Manager, Person); 

Wenn Sie erben bemerkt, wir haben gerade die extend-Methode unserer Inheritance_Manager-Klasse aufgerufen und den subClass-Manager in unserem Fall und dann die baseClass-Person übergeben. Beachten Sie, dass die Reihenfolge hier sehr wichtig ist. Wenn Sie sie vertauschen, funktioniert die Vererbung nicht mehr wie gewünscht. Beachten Sie auch, dass Sie diese Vererbung angeben müssen, bevor Sie unsere Unterklasse definieren können. Jetzt definieren wir unsere Unterklasse:

Wir können weitere Methoden wie die unten hinzufügen. Unsere Manager-Klasse enthält immer die Methoden und Eigenschaften, die in der Person-Klasse definiert sind, weil sie von ihr erbt.

Manager.prototype.lead = function(){ 
    alert('I am a good leader'); 
} 

Jetzt testen sie uns zwei Objekte erstellen lassen, eine aus der Klasse Person und eine von der vererbten Klasse Manager:

var p = new Person(1, 'Joe Tester', 26); 
var pm = new Manager(1, 'Joe Tester', 26, '20.000'); 

Fühlen Sie sich frei im ganzen Code und Kommentare zu erhalten: http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

+2

http://blog.codinghorror.com/-Shall-Call-Item-Somethingmanager/ –

Verwandte Themen