Ich habe eine Basisklasse:
function Monster() {
this.health = 100;
}
Monster.prototype.growl = function() {
console.log("Grr!");
}
Dass ich eine weitere Klasse erweitern und erstellen möchte mit:
function Monkey extends Monster() {
this.bananaCount = 5;
}
Monkey.prototype.eatBanana {
this.bananaCount--;
this.health++; //Accessing variable from parent class monster
this.growl(); //Accessing function from parent class monster
}
Ich habe ziemlich viel recherchiert und es scheint viele komplizierte Lösungen dafür in JavaScript zu geben. Was wäre der einfachste und zuverlässigste Weg, dies in JS zu erreichen?
javascript
oop
object
inheritance
ecmascript-6
Lucas Penney
quelle
quelle
Antworten:
Unten für ES6 aktualisiert
März 2013 und ES5
In diesem MDN-Dokument wird die Erweiterung von Klassen gut beschrieben:
https://developer.mozilla.org/en-US/docs/JavaScript/Introduction_to_Object-Oriented_JavaScript
Insbesondere hier ist jetzt sie damit umgehen:
// define the Person Class function Person() {} Person.prototype.walk = function(){ alert ('I am walking!'); }; Person.prototype.sayHello = function(){ alert ('hello'); }; // define the Student class function Student() { // Call the parent constructor Person.call(this); } // inherit Person Student.prototype = Object.create(Person.prototype); // correct the constructor pointer because it points to Person Student.prototype.constructor = Student; // replace the sayHello method Student.prototype.sayHello = function(){ alert('hi, I am a student'); } // add sayGoodBye method Student.prototype.sayGoodBye = function(){ alert('goodBye'); } var student1 = new Student(); student1.sayHello(); student1.walk(); student1.sayGoodBye(); // check inheritance alert(student1 instanceof Person); // true alert(student1 instanceof Student); // true
Beachten Sie, dass dies
Object.create()
in einigen älteren Browsern, einschließlich IE8, nicht unterstützt wird:Wenn Sie in der Lage sind, diese zu unterstützen, schlägt das verknüpfte MDN-Dokument die Verwendung einer Polyfüllung oder die folgende Annäherung vor:
function createObject(proto) { function ctor() { } ctor.prototype = proto; return new ctor(); }
Die Verwendung dieser Option
Student.prototype = createObject(Person.prototype)
ist der Verwendung vorzuziehennew Person()
, da vermieden wird, dass die Konstruktorfunktion des Elternteils beim Erben des Prototyps aufgerufen wird, und der übergeordnete Konstruktor nur aufgerufen wird, wenn der Konstruktor des Erben aufgerufen wird.Mai 2017 und ES6
Zum Glück haben die JavaScript-Designer unsere Bitten um Hilfe gehört und eine geeignetere Herangehensweise an dieses Problem gewählt.
MDN hat ein weiteres großartiges Beispiel für die Vererbung von ES6-Klassen, aber ich zeige genau die gleichen Klassen wie oben in ES6:
class Person { sayHello() { alert('hello'); } walk() { alert('I am walking!'); } } class Student extends Person { sayGoodBye() { alert('goodBye'); } sayHello() { alert('hi, I am a student'); } } var student1 = new Student(); student1.sayHello(); student1.walk(); student1.sayGoodBye(); // check inheritance alert(student1 instanceof Person); // true alert(student1 instanceof Student); // true
Sauber und verständlich, so wie wir es alle wollen. Denken Sie daran, dass ES6 zwar weit verbreitet ist, aber nicht überall unterstützt wird :
quelle
function Person(n) { this.name = n; }
Student.prototype = new Person();
Diese Zeile führt zu einem Fehler, wenn ich auf den Parameter in der Superklasse zugreife.Student.prototype = Object.create(Person.prototype);
Hier funktioniert die klassische ErstellungsmethodeES6 gibt Ihnen nun die Möglichkeit, die Verwendung der Klasse & erweitert keywords:
Dann lautet Ihr Code:
Sie haben eine Basisklasse:
class Monster{ constructor(){ this.health = 100; } growl() { console.log("Grr!"); } }
Dass Sie eine weitere Klasse erweitern und erstellen möchten mit:
class Monkey extends Monster { constructor(){ super(); //don't forget "super" this.bananaCount = 5; } eatBanana() { this.bananaCount--; this.health++; //Accessing variable from parent class monster this.growl(); //Accessing function from parent class monster } }
quelle
try{}catch(e){}
Blöcke, um dies zu verwalten, und weisen Sie den Endbenutzer an, seinen Browser zu aktualisieren, falls dies erforderlich ist.Versuche dies:
Function.prototype.extends = function(parent) { this.prototype = Object.create(parent.prototype); }; Monkey.extends(Monster); function Monkey() { Monster.apply(this, arguments); // call super }
Bearbeiten: Ich habe hier eine kurze Demo http://jsbin.com/anekew/1/edit . Beachten Sie, dass dies
extends
ein reserviertes Wort in JS ist und Sie möglicherweise Warnungen erhalten, wenn Sie Ihren Code fusseln. Sie können ihn einfach benenneninherits
, das ist, was ich normalerweise mache.Mit diesem Helfer und der Verwendung eines Objekts
props
als einzigem Parameter wird die Vererbung in JS etwas einfacher:Function.prototype.inherits = function(parent) { this.prototype = Object.create(parent.prototype); }; function Monster(props) { this.health = props.health || 100; } Monster.prototype = { growl: function() { return 'Grrrrr'; } }; Monkey.inherits(Monster); function Monkey() { Monster.apply(this, arguments); } var monkey = new Monkey({ health: 200 }); console.log(monkey.health); //=> 200 console.log(monkey.growl()); //=> "Grrrr"
quelle
Monkey
werden dieMonster
Eigenschaften (health
) nicht geerbt . Sie erhalten auch "ReferenceError: Monkey is not defined", wennMonkey
dies vor dem Aufruf nicht definiert wurdeMonkey.extends(Monster)
Wenn Ihnen der Prototyp-Ansatz nicht gefällt, weil er sich nicht wirklich gut OOP-artig verhält, können Sie Folgendes versuchen:
var BaseClass = function() { this.some_var = "foobar"; /** * @return string */ this.someMethod = function() { return this.some_var; } }; var MyClass = new Class({ extends: BaseClass }, function() { /** * @param string value */ this.__construct = function(value) { this.some_var = value; } })
Verwenden der Lightweight-Bibliothek (2k minimiert): https://github.com/haroldiedema/joii
quelle
Ich kann eine Variante vorschlagen, habe gerade im Buch gelesen, es scheint die einfachste:
function Parent() { this.name = 'default name'; }; function Child() { this.address = '11 street'; }; Child.prototype = new Parent(); // child class inherits from Parent Child.prototype.constructor = Child; // constructor alignment var a = new Child(); console.log(a.name); // "default name" trying to reach property of inherited class
quelle
Dies ist eine Erweiterung (entschuldigen Sie das Wortspiel) der Lösung von elclanrs, um Details zu Instanzmethoden aufzunehmen und einen erweiterbaren Ansatz für diesen Aspekt der Frage zu verfolgen. Ich erkenne voll und ganz an, dass dies dank David Flanagans "JavaScript: The Definitive Guide" (teilweise angepasst an diesen Kontext) zusammengestellt wurde. Beachten Sie, dass dies deutlich ausführlicher ist als andere Lösungen, aber langfristig wahrscheinlich davon profitieren würde.
Zuerst verwenden wir Davids einfache "Erweitern" -Funktion, die Eigenschaften in ein bestimmtes Objekt kopiert:
function extend(o,p) { for (var prop in p) { o[prop] = p[prop]; } return o; }
Dann implementieren wir sein Dienstprogramm zur Definition von Unterklassen:
function defineSubclass(superclass, // Constructor of our superclass constructor, // Constructor of our new subclass methods, // Instance methods statics) { // Class properties // Set up the prototype object of the subclass constructor.prototype = Object.create(superclass.prototype); constructor.prototype.constructor = constructor; if (methods) extend(constructor.prototype, methods); if (statics) extend(constructor, statics); return constructor; }
Für die letzte Vorbereitung erweitern wir unseren Funktionsprototyp mit Davids neuem Jiggery-Pokery:
Function.prototype.extend = function(constructor, methods, statics) { return defineSubclass(this, constructor, methods, statics); };
Nachdem wir unsere Monster-Klasse definiert haben, gehen wir wie folgt vor (was für alle neuen Klassen, die wir erweitern / erben möchten, wiederverwendbar ist):
var Monkey = Monster.extend( // constructor function Monkey() { this.bananaCount = 5; Monster.apply(this, arguments); // Superclass() }, // methods added to prototype { eatBanana: function () { this.bananaCount--; this.health++; this.growl(); } } );
quelle
Für die traditionelle Erweiterung können Sie einfach eine Oberklasse als Konstruktorfunktion schreiben und diesen Konstruktor dann auf Ihre geerbte Klasse anwenden.
function AbstractClass() { this.superclass_method = function(message) { // do something }; } function Child() { AbstractClass.apply(this); // Now Child will have superclass_method() }
Beispiel für Angularjs:
http://plnkr.co/edit/eFixlsgF3nJ1LeWUJKsd?p=preview
app.service('noisyThing', ['notify',function(notify){ this._constructor = function() { this.scream = function(message) { message = message + " by " + this.get_mouth(); notify(message); console.log(message); }; this.get_mouth = function(){ return 'abstract mouth'; } } }]) .service('cat', ['noisyThing', function(noisyThing){ noisyThing._constructor.apply(this) this.meow = function() { this.scream('meooooow'); } this.get_mouth = function(){ return 'fluffy mouth'; } }]) .service('bird', ['noisyThing', function(noisyThing){ noisyThing._constructor.apply(this) this.twit = function() { this.scream('fuuuuuuck'); } }])
quelle
Für Autodidakten:
function BaseClass(toBePrivate){ var morePrivates; this.isNotPrivate = 'I know'; // add your stuff } var o = BaseClass.prototype; // add your prototype stuff o.stuff_is_never_private = 'whatever_except_getter_and_setter'; // MiddleClass extends BaseClass function MiddleClass(toBePrivate){ BaseClass.call(this); // add your stuff var morePrivates; this.isNotPrivate = 'I know'; } var o = MiddleClass.prototype = Object.create(BaseClass.prototype); MiddleClass.prototype.constructor = MiddleClass; // add your prototype stuff o.stuff_is_never_private = 'whatever_except_getter_and_setter'; // TopClass extends MiddleClass function TopClass(toBePrivate){ MiddleClass.call(this); // add your stuff var morePrivates; this.isNotPrivate = 'I know'; } var o = TopClass.prototype = Object.create(MiddleClass.prototype); TopClass.prototype.constructor = TopClass; // add your prototype stuff o.stuff_is_never_private = 'whatever_except_getter_and_setter'; // to be continued...
Erstellen Sie "Instanz" mit Getter und Setter:
function doNotExtendMe(toBePrivate){ var morePrivates; return { // add getters, setters and any stuff you want } }
quelle
Zusammenfassung:
Es gibt mehrere Möglichkeiten, um das Problem der Erweiterung einer Konstruktorfunktion um einen Prototyp in Javascript zu lösen. Welche dieser Methoden die "beste" Lösung ist, basiert auf Meinungen. Hier sind jedoch zwei häufig verwendete Methoden, um den Funktionsprototyp eines Konstruktors zu erweitern.
ES 2015 Klassen:
class Monster { constructor(health) { this.health = health } growl () { console.log("Grr!"); } } class Monkey extends Monster { constructor (health) { super(health) // call super to execute the constructor function of Monster this.bananaCount = 5; } } const monkey = new Monkey(50); console.log(typeof Monster); console.log(monkey);
Der obige Ansatz der Verwendung von
ES 2015
Klassen ist nichts anderes als syntaktischer Zucker über das prototypische Vererbungsmuster in Javascript. Hier das erste Protokoll, in dem wir auswertentypeof Monster
, können wir beobachten, dass dies eine Funktion ist. Dies liegt daran, dass Klassen nur Konstruktorfunktionen unter der Haube sind. Trotzdem mag Ihnen diese Art der Implementierung der prototypischen Vererbung vielleicht und Sie sollten sie definitiv lernen. Es wird in wichtigen Frameworks wieReactJS
und verwendetAngular2+
.Werksfunktion mit
Object.create()
:function makeMonkey (bananaCount) { // here we define the prototype const Monster = { health: 100, growl: function() { console.log("Grr!");} } const monkey = Object.create(Monster); monkey.bananaCount = bananaCount; return monkey; } const chimp = makeMonkey(30); chimp.growl(); console.log(chimp.bananaCount);
Diese Methode verwendet die
Object.create()
Methode, die ein Objekt verwendet, das der Prototyp des neu erstellten Objekts ist, das es zurückgibt. Daher erstellen wir zuerst das Prototypobjekt in dieser Funktion und rufen dann auf,Object.create()
das ein leeres Objekt mit der__proto__
auf das Monster-Objekt festgelegten Eigenschaft zurückgibt . Danach können wir alle Eigenschaften des Objekts initialisieren. In diesem Beispiel weisen wir dem neu erstellten Objekt den Bananacount zu.quelle
Die absolut minimale (und korrekte, im Gegensatz zu vielen der obigen Antworten) Version ist:
function Monkey(param){ this.someProperty = param; } Monkey.prototype = Object.create(Monster.prototype); Monkey.prototype.eatBanana = function(banana){ banana.eat() }
Das ist alles. Sie können hier die längere Erklärung lesen
quelle