Wie erbe ich von einer Klasse in Javascript?

99

In PHP / Java kann man:

class Sub extends Base
{
}

Und automatisch werden alle öffentlichen / geschützten Methoden, Eigenschaften, Felder usw. der Super-Klasse Teil der Sub-Klasse, die bei Bedarf überschrieben werden können.

Was ist das Äquivalent dazu in Javascript?

Klicken Sie auf Upvote
quelle
1
Werfen
Darin Dimitrov
Funktioniert dieser Crockford-Weg noch? ZParenizor.inherits (Parenizor);
Loren Shqipognja
Siehe auch: JavaScript Extending Class
user56reinstatemonica8

Antworten:

80

Ich habe prototypemeine Vorgehensweise jetzt geändert. Ich versuche, die Verwendung von Konstruktorfunktionen und deren Eigenschaften zu vermeiden , aber meine alte Antwort aus dem Jahr 2010 ist immer noch ganz unten. Ich bevorzuge jetzt Object.create(). Object.createist in allen modernen Browsern verfügbar.

Ich sollte beachten, dass dies Object.createnormalerweise viel langsamer ist als die Verwendung newmit einem Funktionskonstruktor.

//The prototype is just an object when you use `Object.create()`
var Base = {};

//This is how you create an instance:
var baseInstance = Object.create(Base);

//If you want to inherit from "Base":
var subInstance = Object.create(Object.create(Base));

//Detect if subInstance is an instance of Base:
console.log(Base.isPrototypeOf(subInstance)); //True

jsfiddle

Einer der großen Vorteile der Verwendung von Object.create besteht darin , dass Sie ein defineProperties- Argument übergeben können, mit dem Sie maßgeblich steuern können, wie auf Eigenschaften der Klasse zugegriffen und diese aufgelistet werden können. Außerdem verwende ich Funktionen zum Erstellen von Instanzen, die als solche dienen Konstruktoren in gewisser Weise, da Sie die Initialisierung am Ende durchführen können, anstatt nur die Instanz zurückzugeben.

var Base = {};

function createBase() {
  return Object.create(Base, {
    doSomething: {
       value: function () {
         console.log("Doing something");
       },
    },
  });
}

var Sub = createBase();

function createSub() {
  return Object.create(Sub, {
    doSomethingElse: {
      value: function () {
        console.log("Doing something else");
      },
    },
  }); 
}

var subInstance = createSub();
subInstance.doSomething(); //Logs "Doing something"
subInstance.doSomethingElse(); //Logs "Doing something else"
console.log(Base.isPrototypeOf(subInstance)); //Logs "true"
console.log(Sub.isPrototypeOf(subInstance)); //Logs "true

jsfiddle

Dies ist meine ursprüngliche Antwort aus dem Jahr 2010:

function Base ( ) {
  this.color = "blue";
}

function Sub ( ) {

}
Sub.prototype = new Base( );
Sub.prototype.showColor = function ( ) {
 console.log( this.color );
}

var instance = new Sub ( );
instance.showColor( ); //"blue"
Björn
quelle
5
Wie wäre es mit sub.prototype.constructor value? Ich denke, es sollte auch auf Unterwert gesetzt werden.
Maximus
Abgesehen davon, dass Sie reservierte Schlüsselwörter ('super') als Klassennamen verwenden, konnte ich Ihr Beispiel nicht zum Laufen bringen: jsbin.com/ixiyet/8/edit
MOnsDaR
@ MOnsDaR Ich habe es in Base
Björn
Wenn ich alert()sehe, was instance.showColor()zurückkommt, bekomme ich immer noch undefined. jsbin.com/uqalin/1
MOnsDaR
1
@MOnsDaR Das liegt daran, dass es Konsolenprotokolle gibt und nichts zurückgibt, damit eine Warnung angezeigt wird. Sehen Sie eine return-Anweisung in showColor?
Björn
190

In JavaScript gibt es keine Klassen, aber Sie können Vererbung und Wiederverwendung von Verhalten auf viele Arten erhalten:

Pseudoklassische Vererbung (durch Prototyping):

function Super () {
  this.member1 = 'superMember1';
}
Super.prototype.member2 = 'superMember2';

function Sub() {
  this.member3 = 'subMember3';
  //...
}
Sub.prototype = new Super();

Sollte mit dem newBediener verwendet werden:

var subInstance = new Sub();

Funktionsanwendung oder "Konstruktorkettung":

function Super () {
  this.member1 = 'superMember1';
  this.member2 = 'superMember2';
}


function Sub() {
  Super.apply(this, arguments);
  this.member3 = 'subMember3';
}

Dieser Ansatz sollte auch beim newBediener angewendet werden:

var subInstance = new Sub();

Der Unterschied zum ersten Beispiel besteht darin, dass, wenn wir applyden SuperKonstruktor zum thisObjekt im Inneren Subhinzufügen, die Eigenschaften hinzugefügt werden, die thison zugewiesen wurden Super, direkt in der neuen Instanz, z. B. subInstancedie Eigenschaften enthält member1und member2direkt (subInstance.hasOwnProperty('member1') == true; ).

Im ersten Beispiel werden diese Eigenschaften über die Prototypenkette erreicht , sie existieren auf einem internen [[Prototype]]Objekt.

Parasitäre Vererbung oder Leistungskonstruktoren:

function createSuper() {
  var obj = {
    member1: 'superMember1',
    member2: 'superMember2'
  };

  return obj;
}

function createSub() {
  var obj = createSuper();
  obj.member3 = 'subMember3';
  return obj;
}

Dieser Ansatz basiert im Wesentlichen auf "Objekterweiterung", Sie müssen den newOperator nicht verwenden, und wie Sie sehen können, ist das thisSchlüsselwort nicht beteiligt.

var subInstance = createSub();

ECMAScript 5th Ed. Object.createMethode:

// Check if native implementation available
if (typeof Object.create !== 'function') {
  Object.create = function (o) {
    function F() {}  // empty constructor
    F.prototype = o; // set base object as prototype
    return new F();  // return empty object with right [[Prototype]]
  };
}

var superInstance = {
  member1: 'superMember1',
  member2: 'superMember2'
};

var subInstance = Object.create(superInstance);
subInstance.member3 = 'subMember3';

Das obige Verfahren ist eine von Crockford vorgeschlagene prototypische Vererbungstechnik .

Objektinstanzen erben von anderen Objektinstanzen, das war's.

Diese Technik kann besser sein als eine einfache "Objekterweiterung", da die geerbten Eigenschaften nicht über alle neuen Objektinstanzen kopiert werden, da das Basisobjekt als das [[Prototype]]des erweiterten Objekts festgelegt ist und im obigen Beispiel subInstancenur die member3Eigenschaft enthält.

CMS
quelle
3
Verwenden Sie keine Instanzen für die Vererbung. Verwenden Sie ES5 Object.create()oder eine benutzerdefinierte clone()Funktion (z. B. mercurial.intuxication.org/hg/js-hacks/raw-file/tip/clone.js ), um direkt vom Prototypobjekt zu erben. Eine Erklärung finden Sie in den Kommentaren zu stackoverflow.com/questions/1404559/…
Christoph
Danke @Christoph, ich wollte gerade die Object.createMethode erwähnen :)
CMS
1
Dies ist keine ordnungsgemäße Vererbung, da Sie Super-Instanzmitglieder auf dem Prototyp von Sub haben. Daher teilen sich alle Instanzen des Sub dieselbe member1Variable, was überhaupt nicht wünschenswert ist. Natürlich können sie es umschreiben, aber das macht einfach keinen Sinn. github.com/dotnetwise/Javascript-FastClass ist eine bessere Zuckerlösung.
Adaptabi
Hallo @CMS, können Sie bitte erklären, warum ich im ersten Beispiel eine Instanz der übergeordneten Klasse erstellen muss, um die Vererbung für die Unterklasse einzurichten? Ich spreche von dieser Zeile : Sub.prototype = new Super();. Was ist, wenn beide Klassen während der Skriptausführung niemals verwendet werden? Es sieht nach einem Leistungsproblem aus. Warum muss ich eine übergeordnete Klasse erstellen, wenn die untergeordnete Klasse nicht tatsächlich verwendet wird? Können Sie bitte näher darauf eingehen? Hier ist die einfache Demonstration des Problems: jsfiddle.net/slavafomin/ZeVL2 Danke!
Slava Fomin II
In allen Beispielen - mit Ausnahme des letzten - gibt es eine "Klasse" für Super und eine "Klasse" für Sub, und dann erstellen Sie eine Instanz des Sub. Können Sie ein vergleichbares Beispiel für das Object.create-Beispiel hinzufügen?
Luke
48

Für diejenigen, die diese Seite im Jahr 2019 oder später erreichen

Mit der neuesten Version des ECMAScript-Standards (ES6) können Sie das Schlüsselwort verwenden class.

Beachten Sie, dass die Klassendefinition keine reguläre ist object . Daher gibt es keine Kommas zwischen den Klassenmitgliedern. Um eine Instanz einer Klasse zu erstellen, müssen Sie das newSchlüsselwort verwenden. Verwenden Sie zum Erben von einer Basisklasse extends:

class Vehicle {
   constructor(name) {
      this.name = name;
      this.kind = 'vehicle';
   }
   getName() {
      return this.name;
   }   
}

// Create an instance
var myVehicle = new Vehicle('rocky');
myVehicle.getName(); // => 'rocky'

Verwenden Sie zum Erben von einer Basisklasse extends:

class Car extends Vehicle {
   constructor(name) {
      super(name);
      this.kind = 'car'
   }
}

var myCar = new Car('bumpy');

myCar.getName(); // => 'bumpy'
myCar instanceof Car; // => true
myCar instanceof Vehicle; // => true

Aus der abgeleiteten Klasse können Sie Super von jedem Konstruktor oder jeder Methode verwenden, um auf die Basisklasse zuzugreifen:

  • Verwenden Sie zum Aufrufen des übergeordneten Konstruktors super().
  • Um ein anderes Mitglied anzurufen, verwenden Sie beispielsweise super.getName() .

Es gibt mehr zu Klassen. Wenn Sie tiefer in das Thema eintauchen möchten, empfehle ich “ Klassen in ECMAScript 6 “ von Dr. Axel Rauschmayer. *

Quelle

Merlin
quelle
1
Unter der Haube classund extends(äußerst nützlicher) Syntaxzucker
Ciro Santilli 郝海东 冠状 病 六四 事件 20
Nur zu Ihrer Information 'instance.name' hier gibt 'mycar.name' den Namen der Klasse zurück. Dies ist ein Standardverhalten von ES6 und ESnext. Hier für mycar.name wird 'Fahrzeug' zurückgeben
Shiljo Paulson
7

Nun, in JavaScript gibt es keine "Klassenvererbung", sondern nur eine "Prototypvererbung". Sie machen also keine Klasse "LKW" und markieren sie dann als Unterklasse von "Automobil". Stattdessen machen Sie ein Objekt "Jack" und sagen, dass es "John" als Prototyp verwendet. Wenn John weiß, wie viel "4 + 4" ist, dann weiß Jack es auch.

Ich schlage vor, dass Sie Douglas Crockfords Artikel über die Vererbung von Prototypen hier lesen: http://javascript.crockford.com/prototypal.html Er zeigt auch, wie Sie JavaScript wie in anderen OO-Sprachen "ähnlich" vererben können, und erklärt dies dann bedeutet eigentlich, JavaScript so zu brechen, dass es nicht verwendet werden sollte.

Naivisten
quelle
Nehmen wir an, Jacks Prototyp ist John. Zur Laufzeit habe ich John eine Eigenschaft / ein Verhalten hinzugefügt. Bekomme ich diese Eigenschaft / dieses Verhalten von Jack?
Ram Bavireddi
Das wirst du sicher. Auf diese Weise fügen Benutzer normalerweise allen Zeichenfolgenobjekten die Methode "trim ()" hinzu (sie ist nicht integriert). Ein Beispiel finden Sie hier: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ …
Naivisten
6

Ich finde dieses Zitat am aufschlussreichsten:

Im Wesentlichen ist eine JavaScript- "Klasse" nur ein Funktionsobjekt, das als Konstruktor und ein angefügtes Prototypobjekt dient. ( Quelle: Guru Katz )

Ich verwende lieber Konstruktoren als Objekte, daher bin ich Teil der hier von CMS beschriebenen "pseudoklassischen Vererbungsmethode" . Hier ist ein Beispiel für die Mehrfachvererbung mit einer Prototypkette :

// Lifeform "Class" (Constructor function, No prototype)
function Lifeform () {
    this.isLifeform = true;
}

// Animal "Class" (Constructor function + prototype for inheritance)
function Animal () {
    this.isAnimal = true;
}
Animal.prototype = new Lifeform();

// Mammal "Class" (Constructor function + prototype for inheritance)
function Mammal () {
    this.isMammal = true;
}
Mammal.prototype = new Animal();

// Cat "Class" (Constructor function + prototype for inheritance)
function Cat (species) {
    this.isCat = true;
    this.species = species
}
Cat.prototype = new Mammal();

// Make an instance object of the Cat "Class"
var tiger = new Cat("tiger");

console.log(tiger);
// The console outputs a Cat object with all the properties from all "classes"

console.log(tiger.isCat, tiger.isMammal, tiger.isAnimal, tiger.isLifeform);
// Outputs: true true true true

// You can see that all of these "is" properties are available in this object
// We can check to see which properties are really part of the instance object
console.log( "tiger hasOwnProperty: "
    ,tiger.hasOwnProperty("isLifeform") // false
    ,tiger.hasOwnProperty("isAnimal")   // false
    ,tiger.hasOwnProperty("isMammal")   // false
    ,tiger.hasOwnProperty("isCat")      // true
);

// New properties can be added to the prototypes of any
// of the "classes" above and they will be usable by the instance
Lifeform.prototype.A    = 1;
Animal.prototype.B      = 2;
Mammal.prototype.C      = 3;
Cat.prototype.D         = 4;

console.log(tiger.A, tiger.B, tiger.C, tiger.D);
// Console outputs: 1 2 3 4

// Look at the instance object again
console.log(tiger);
// You'll see it now has the "D" property
// The others are accessible but not visible (console issue?)
// In the Chrome console you should be able to drill down the __proto__ chain
// You can also look down the proto chain with Object.getPrototypeOf
// (Equivalent to tiger.__proto__)
console.log( Object.getPrototypeOf(tiger) );  // Mammal 
console.log( Object.getPrototypeOf(Object.getPrototypeOf(tiger)) ); // Animal
// Etc. to get to Lifeform

Hier ist eine weitere gute Ressource von MDN , und hier ist eine jsfiddle, damit Sie sie ausprobieren können .

Luke
quelle
4

Die Vererbung von Javascript unterscheidet sich ein wenig von Java und PHP, da es nicht wirklich Klassen gibt. Stattdessen verfügt es über Prototypobjekte, die Methoden und Elementvariablen bereitstellen. Sie können diese Prototypen verketten, um die Objektvererbung bereitzustellen. Das häufigste Muster, das ich bei der Untersuchung dieser Frage gefunden habe, ist im Mozilla Developer Network beschrieben . Ich habe ihr Beispiel aktualisiert, um einen Aufruf einer Superklassenmethode aufzunehmen und das Protokoll in einer Warnmeldung anzuzeigen:

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

// superclass method
Shape.prototype.move = function(x, y) {
  this.x += x;
  this.y += y;
  log += 'Shape moved.\n';
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); // call super constructor.
}

// subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

// Override method
Rectangle.prototype.move = function(x, y) {
  Shape.prototype.move.call(this, x, y); // call superclass method
  log += 'Rectangle moved.\n';
}

var log = "";
var rect = new Rectangle();

log += ('Is rect an instance of Rectangle? ' + (rect instanceof Rectangle) + '\n'); // true
log += ('Is rect an instance of Shape? ' + (rect instanceof Shape) + '\n'); // true
rect.move(1, 1); // Outputs, 'Shape moved.'
alert(log);

Persönlich finde ich die Vererbung in Javascript umständlich, aber dies ist die beste Version, die ich gefunden habe.

Don Kirkby
quelle
3

Sie können nicht (im klassischen Sinne). Javascript ist eine prototypische Sprache. Sie werden feststellen, dass Sie in Javascript niemals eine "Klasse" deklarieren. Sie definieren lediglich den Status und die Methoden eines Objekts. Um eine Vererbung zu erzeugen, nehmen Sie ein Objekt und erstellen einen Prototyp. Der Prototyp wird um neue Funktionen erweitert.

DC
quelle
1

Sie können .inheritWithund .fastClass Bibliothek verwenden . Es ist schneller als die meisten gängigen Bibliotheken und manchmal sogar schneller als die native Version.

Sehr einfach zu bedienen:

function Super() {
   this.member1 = "superMember";//instance member
}.define({ //define methods on Super's prototype
   method1: function() { console.log('super'); } //prototype member
}.defineStatic({ //define static methods directly on Super function 
   staticMethod1: function() { console.log('static method on Super'); }
});

var Sub = Super.inheritWith(function(base, baseCtor) {
   return {
      constructor: function() {//the Sub constructor that will be returned to variable Sub
         this.member3 = 'subMember3'; //instance member on Sub
         baseCtor.apply(this, arguments);//call base construcor and passing all incoming arguments
      },
      method1: function() { 
         console.log('sub'); 
         base.method1.apply(this, arguments); //call the base class' method1 function
      }
}

Verwendung

var s = new Sub();
s.method1(); //prints:
//sub 
//super
Adaptabi
quelle
1
function Person(attr){
  this.name = (attr && attr.name)? attr.name : undefined;
  this.birthYear = (attr && attr.birthYear)? attr.birthYear : undefined;

  this.printName = function(){
    console.log(this.name);
  }
  this.printBirthYear = function(){
    console.log(this.birthYear);
  }
  this.print = function(){
    console.log(this.name + '(' +this.birthYear+ ')');
  }
}

function PersonExt(attr){
  Person.call(this, attr);

  this.print = function(){
    console.log(this.name+ '-' + this.birthYear);
  }
  this.newPrint = function(){
    console.log('New method');
  }
}
PersonExt.prototype = new Person();

// Init object and call methods
var p = new Person({name: 'Mr. A', birthYear: 2007});
// Parent method
p.print() // Mr. A(2007)
p.printName() // Mr. A

var pExt = new PersonExt({name: 'Mr. A', birthYear: 2007});
// Overwriten method
pExt.print() // Mr. A-2007
// Extended method
pExt.newPrint() // New method
// Parent method
pExt.printName() // Mr. A
nnattawat
quelle
1

Nachdem ich viele Beiträge gelesen hatte, kam ich auf diese Lösung ( jsfiddle hier ). Meistens brauche ich nichts Anspruchsvolleres

var Class = function(definition) {
    var base = definition.extend || null;
    var construct = definition.construct || definition.extend || function() {};

    var newClass = function() { 
        this._base_ = base;        
        construct.apply(this, arguments);
    }

    if (definition.name) 
        newClass._name_ = definition.name;

    if (definition.extend) {
        var f = function() {}       
        f.prototype = definition.extend.prototype;      
        newClass.prototype = new f();   
        newClass.prototype.constructor = newClass;
        newClass._extend_ = definition.extend;      
        newClass._base_ = definition.extend.prototype;         
    }

    if (definition.statics) 
        for (var n in definition.statics) newClass[n] = definition.statics[n];          

    if (definition.members) 
        for (var n in definition.members) newClass.prototype[n] = definition.members[n];    

    return newClass;
}


var Animal = Class({

    construct: function() {        
    },

    members: {

        speak: function() {
            console.log("nuf said");                        
        },

        isA: function() {        
            return "animal";           
        }        
    }
});


var Dog = Class({  extend: Animal,

    construct: function(name) {  
        this._base_();        
        this.name = name;
    },

    statics: {
        Home: "House",
        Food: "Meat",
        Speak: "Barks"
    },

    members: {
        name: "",

        speak: function() {
            console.log( "ouaf !");         
        },

        isA: function(advice) {
           return advice + " dog -> " + Dog._base_.isA.call(this);           
        }        
    }
});


var Yorkshire = Class({ extend: Dog,

    construct: function(name,gender) {
        this._base_(name);      
        this.gender = gender;
    },

    members: {
        speak: function() {
            console.log( "ouin !");           
        },

        isA: function(advice) {         
           return "yorkshire -> " + Yorkshire._base_.isA.call(this,advice);       
        }        
    }
});


var Bulldog = function() { return _class_ = Class({ extend: Dog,

    construct: function(name) {
        this._base_(name);      
    },

    members: {
        speak: function() {
            console.log( "OUAF !");           
        },

        isA: function(advice) {         
           return "bulldog -> " + _class_._base_.isA.call(this,advice);       
        }        
    }
})}();


var animal = new Animal("Maciste");
console.log(animal.isA());
animal.speak();

var dog = new Dog("Sultan");
console.log(dog.isA("good"));
dog.speak();

var yorkshire = new Yorkshire("Golgoth","Male");
console.log(yorkshire.isA("bad"));
yorkshire.speak();

var bulldog = new Bulldog("Mike");
console.log(bulldog.isA("nice"));
bulldog.speak();
Chauwel
quelle
1

Dank der Antwort von CMS und nachdem ich eine Weile mit Prototyp und Object.create herumgespielt hatte und was nicht, konnte ich eine saubere Lösung für meine Vererbung mit apply finden, wie hier gezeigt:

var myNamespace = myNamespace || (function() {
    return {

        BaseClass: function(){
            this.someBaseProperty = "someBaseProperty";
            this.someProperty = "BaseClass";
            this.someFunc = null;
        },

        DerivedClass:function(someFunc){
            myNamespace.BaseClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "DerivedClass";
        },

        MoreDerivedClass:function(someFunc){
            myNamespace.DerivedClass.apply(this, arguments);
            this.someFunc = someFunc;
            this.someProperty = "MoreDerivedClass";
        }
    };
})();
pasx
quelle
1
function Base() {
    this.doSomething = function () {
    }
}

function Sub() {
    Base.call(this); // inherit Base's method(s) to this instance of Sub
}

var sub = new Sub();
sub.doSomething();
Kai Hartmann
quelle
2
Bitte geben Sie nicht nur die Postleitzahl ein, sondern erklären Sie, was es tut und wie es die Frage beantwortet.
Patrick Hund
1

ES6-Klassen:

Javascript hat keine Klassen. Klassen in Javascript sind nur syntaktischer Zucker, der auf dem prototypischen Vererbungsmuster von Javascript aufbaut. Sie können JS verwenden class, um die prototypische Vererbung zu erzwingen. Es ist jedoch wichtig zu wissen, dass Sie tatsächlich noch Konstruktorfunktionen unter der Haube verwenden.

Diese Konzepte gelten auch, wenn Sie es6mit dem Schlüsselwort extenses von einer 'Klasse' aus erweitern. Dies schafft nur ein zusätzliches Glied in der Prototypenkette. Das__proto__

Beispiel:

class Animal {
  makeSound () {
    console.log('animalSound');
  }
}

class Dog extends Animal {
   makeSound () {
    console.log('Woof');
  }
}


console.log(typeof Dog)  // classes in JS are just constructor functions under the hood

const dog = new Dog();

console.log(dog.__proto__ === Dog.prototype);   
// First link in the prototype chain is Dog.prototype

console.log(dog.__proto__.__proto__ === Animal.prototype);  
// Second link in the prototype chain is Animal.prototype
// The extends keyword places Animal in the prototype chain
// Now Dog 'inherits' the makeSound property from Animal

Object.create ()

Object.create()ist auch eine Möglichkeit, eine Vererbung in JS in Javascript zu erstellen. Object.create()ist eine Funktion, die ein neues Objekt erstellt und ein vorhandenes Objekt als Argument verwendet. Es wird das Objekt, das als Argument empfangen wurde, dem zuweisen__proto__ Eigenschaft des neu erstellten Objekts . Auch hier ist es wichtig zu erkennen, dass wir an das prototypische Vererbungsparadigma gebunden sind, das JS verkörpert.

Beispiel:

const Dog = {
  fluffy: true,
  bark: () => {
      console.log('woof im a relatively cute dog or something else??');
  }
};

const dog = Object.create(Dog);

dog.bark();

Willem van der Veen
quelle
0

Sie können nicht von einer Klasse in JavaScript erben, da es in JavaScript keine Klassen gibt.

Jörg W Mittag
quelle