Wie erstelle ich eine abstrakte Basisklasse in JavaScript?

Lesezeit: 9 Minuten

Benutzer-Avatar
Sabja

Ist es möglich, eine abstrakte Basisklasse in JavaScript zu simulieren? Wie geht das am elegantesten?

Angenommen, ich möchte etwas tun wie: –

var cat = new Animal('cat');
var dog = new Animal('dog');

cat.say();
dog.say();

Es sollte ausgeben: ‘bellen’, ‘miau’

  • verwandt: So erstellen Sie eine abstrakte Basisklasse in JavaScript, die nicht instanziiert werden kann

    – Bergi

    11. Februar 2020 um 12:54 Uhr

Benutzer-Avatar
Jordan

Eine einfache Möglichkeit, eine abstrakte Klasse zu erstellen, ist die folgende:

/**
 @constructor
 @abstract
 */
var Animal = function() {
    if (this.constructor === Animal) {
      throw new Error("Can't instantiate abstract class!");
    }
    // Animal initialization...
};

/**
 @abstract
 */
Animal.prototype.say = function() {
    throw new Error("Abstract method!");
}

Das Animal “Klasse” und die say Methode sind abstrakt.

Das Erstellen einer Instanz würde einen Fehler auslösen:

new Animal(); // throws

So “erben” Sie davon:

var Cat = function() {
    Animal.apply(this, arguments);
    // Cat initialization...
};
Cat.prototype = Object.create(Animal.prototype);
Cat.prototype.constructor = Cat;

Cat.prototype.say = function() {
    console.log('meow');
}

Dog sieht genauso aus.

Und so spielt sich Ihr Szenario ab:

var cat = new Cat();
var dog = new Dog();

cat.say();
dog.say();

Geige hier (Schauen Sie sich die Konsolenausgabe an).

  • Können Sie bitte Zeile für Zeile erklären, wenn es Ihnen nichts ausmacht, da ich neu bei OOPs bin. Vielen Dank!

    – Entwickler

    17. Oktober 2014 um 4:06 Uhr

  • @undefined: Um es zu verstehen, schlage ich vor, dass Sie die prototypische Vererbung in Javascript nachschlagen: Dies ist ein guter Ratgeber.

    – Jordan

    17. Oktober 2014 um 16:58 Uhr

  • Danke für die Antwort.. werde den Link durchgehen.

    – Entwickler

    20. Oktober 2014 um 3:46 Uhr


  • Das Wichtigste dabei ist, dass im allerersten Code-Snippet ein Fehler ausgegeben wird. Sie können auch eine Warnung ausgeben oder einen Nullwert anstelle eines Objekts zurückgeben, um die Anwendungsausführung fortzusetzen, es hängt wirklich von Ihrer Implementierung ab. Dies ist meiner Meinung nach der richtige Weg, um abstrakte JS-“Klassen” zu implementieren.

    – Dudewad

    18. Januar 2016 um 21:36 Uhr

  • @G1P: Das ist die übliche Art, einen “Super-Class-Konstruktor” in Javascript auszuführen, und es muss manuell so gemacht werden.

    – Jordan

    12. August 2016 um 18:44 Uhr

Benutzer-Avatar
Daniel Possamai

JavaScript-Klassen und -Vererbung (ES6)

Gemäß ES6 können Sie JavaScript-Klassen und Vererbung verwenden, um das zu erreichen, was Sie benötigen.

JavaScript-Klassen, die in ECMAScript 2015 eingeführt wurden, sind in erster Linie syntaktischer Zucker gegenüber der bestehenden prototypbasierten Vererbung von JavaScript.

Bezug: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes

Zuerst definieren wir unsere abstrakte Klasse. Diese Klasse kann nicht instanziiert, aber erweitert werden. Wir können auch Funktionen definieren, die in allen Klassen implementiert werden müssen, die diese erweitern.

/**
 * Abstract Class Animal.
 *
 * @class Animal
 */
class Animal {

  constructor() {
    if (this.constructor == Animal) {
      throw new Error("Abstract classes can't be instantiated.");
    }
  }

  say() {
    throw new Error("Method 'say()' must be implemented.");
  }

  eat() {
    console.log("eating");
  }
}

Danach können wir unsere konkreten Klassen erstellen. Diese Klassen erben alle Funktionen und Verhaltensweisen von der abstrakten Klasse.

/**
 * Dog.
 *
 * @class Dog
 * @extends {Animal}
 */
class Dog extends Animal {
  say() {
    console.log("bark");
  }
}

/**
 * Cat.
 *
 * @class Cat
 * @extends {Animal}
 */
class Cat extends Animal {
  say() {
    console.log("meow");
  }
}

/**
 * Horse.
 *
 * @class Horse
 * @extends {Animal}
 */
class Horse extends Animal {}

Und die Ergebnisse…

// RESULTS

new Dog().eat(); // eating
new Cat().eat(); // eating
new Horse().eat(); // eating

new Dog().say(); // bark
new Cat().say(); // meow
new Horse().say(); // Error: Method say() must be implemented.

new Animal(); // Error: Abstract classes can't be instantiated.

  • Dies sollte ab 2021 die Top-Antwort sein.

    – Artur Barseghyan

    17. November 2021 um 22:52 Uhr

Benutzer-Avatar
etwas

Meinst du sowas:

function Animal() {
  //Initialization for all Animals
}

//Function and properties shared by all instances of Animal
Animal.prototype.init=function(name){
  this.name=name;
}
Animal.prototype.say=function(){
    alert(this.name + " who is a " + this.type + " says " + this.whattosay);
}
Animal.prototype.type="unknown";

function Cat(name) {
    this.init(name);

    //Make a cat somewhat unique
    var s="";
    for (var i=Math.ceil(Math.random()*7); i>=0; --i) s+="e";
    this.whattosay="Me" + s +"ow";
}
//Function and properties shared by all instances of Cat    
Cat.prototype=new Animal();
Cat.prototype.type="cat";
Cat.prototype.whattosay="meow";


function Dog() {
    //Call init with same arguments as Dog was called with
    this.init.apply(this,arguments);
}

Dog.prototype=new Animal();
Dog.prototype.type="Dog";
Dog.prototype.whattosay="bark";
//Override say.
Dog.prototype.say = function() {
        this.openMouth();
        //Call the original with the exact same arguments
        Animal.prototype.say.apply(this,arguments);
        //or with other arguments
        //Animal.prototype.say.call(this,"some","other","arguments");
        this.closeMouth();
}

Dog.prototype.openMouth=function() {
   //Code
}
Dog.prototype.closeMouth=function() {
   //Code
}

var dog = new Dog("Fido");
var cat1 = new Cat("Dash");
var cat2 = new Cat("Dot");


dog.say(); // Fido the Dog says bark
cat1.say(); //Dash the Cat says M[e]+ow
cat2.say(); //Dot the Cat says M[e]+ow


alert(cat instanceof Cat) // True
alert(cat instanceof Dog) // False
alert(cat instanceof Animal) // True

  • Vielleicht habe ich es verpasst. Wo ist die Basisklasse (Animal) abstrakt?

    – Hundehaare

    15. September 2014 um 22:39 Uhr

  • @HairOfTheDog Ja, Sie haben übersehen, dass dies vor ungefähr fünf Jahren beantwortet wurde, dass Javascript damals keine abstrakten Klassen hatte, dass die Frage nach einem Weg war simulieren es (whattosay ist nicht definiert in Tier) und dass diese Antwort eindeutig fragt, ob die vorgeschlagene Antwort das war, wonach der Fragesteller gesucht hat. Es erhebt nicht den Anspruch, eine Lösung für abstrakte Klassen in Javascript zu geben. Der Fragesteller hat sich nicht die Mühe gemacht, mir oder irgendjemand anderem zu antworten, also habe ich keine Ahnung, ob es für ihn funktioniert hat. Es tut mir leid, wenn eine fünf Jahre alte vorgeschlagene Antwort auf die Frage eines anderen für Sie nicht funktioniert hat.

    – etwas

    16. September 2014 um 0:49 Uhr

Vielleicht möchten Sie sich die Basisklasse von Dean Edwards ansehen: http://dean.edwards.name/weblog/2006/03/base/

Alternativ gibt es dieses Beispiel / Artikel von Douglas Crockford zur klassischen Vererbung in JavaScript: http://www.crockford.com/javascript/inheritance.html

Ist es möglich, eine abstrakte Basisklasse in JavaScript zu simulieren?

Sicherlich. Es gibt ungefähr tausend Möglichkeiten, Klassen-/Instanzsysteme in JavaScript zu implementieren. Hier ist eine:

// Classes magic. Define a new class with var C= Object.subclass(isabstract),
// add class members to C.prototype,
// provide optional C.prototype._init() method to initialise from constructor args,
// call base class methods using Base.prototype.call(this, ...).
//
Function.prototype.subclass= function(isabstract) {
    if (isabstract) {
        var c= new Function(
            'if (arguments[0]!==Function.prototype.subclass.FLAG) throw(\'Abstract class may not be constructed\'); '
        );
    } else {
        var c= new Function(
            'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
            'if (arguments[0]!==Function.prototype.subclass.FLAG && this._init) this._init.apply(this, arguments); '
        );
    }
    if (this!==Object)
        c.prototype= new this(Function.prototype.subclass.FLAG);
    return c;
}
Function.prototype.subclass.FLAG= new Object();

var cat = new Animal(‘cat’);

Das ist natürlich nicht wirklich eine abstrakte Basisklasse. Meinst du sowas wie:

var Animal= Object.subclass(true); // is abstract
Animal.prototype.say= function() {
    window.alert(this._noise);
};

// concrete classes
var Cat= Animal.subclass();
Cat.prototype._noise="meow";
var Dog= Animal.subclass();
Dog.prototype._noise="bark";

// usage
var mycat= new Cat();
mycat.say(); // meow!
var mygiraffe= new Animal(); // error!

  • Warum verwenden Sie das böse neue Function(…)-Konstrukt? Wäre das nicht var c = function () { … }; sei besser?

    – fionbio

    28. Februar 2009 um 14:15 Uhr

  • „var c= function() {…}“ würde einen Abschluss über alles in subclass() oder einem anderen enthaltenden Bereich erstellen. Wahrscheinlich nicht wichtig, aber ich wollte es von potenziell unerwünschten übergeordneten Bereichen freihalten. Der reine Text-Konstruktor Function() vermeidet Closures.

    – Bobin

    28. Februar 2009 um 16:27 Uhr

Animal = function () { throw "abstract class!" }
Animal.prototype.name = "This animal";
Animal.prototype.sound = "...";
Animal.prototype.say = function() {
    console.log( this.name + " says: " + this.sound );
}

Cat = function () {
    this.name = "Cat";
    this.sound = "meow";
}

Dog = function() {
    this.name = "Dog";
    this.sound  = "woof";
}

Cat.prototype = Object.create(Animal.prototype);
Dog.prototype = Object.create(Animal.prototype);

new Cat().say();    //Cat says: meow
new Dog().say();    //Dog says: woof 
new Animal().say(); //Uncaught abstract class! 

  • Warum verwenden Sie das böse neue Function(…)-Konstrukt? Wäre das nicht var c = function () { … }; sei besser?

    – fionbio

    28. Februar 2009 um 14:15 Uhr

  • „var c= function() {…}“ würde einen Abschluss über alles in subclass() oder einem anderen enthaltenden Bereich erstellen. Wahrscheinlich nicht wichtig, aber ich wollte es von potenziell unerwünschten übergeordneten Bereichen freihalten. Der reine Text-Konstruktor Function() vermeidet Closures.

    – Bobin

    28. Februar 2009 um 16:27 Uhr

Die Frage ist ziemlich alt, aber ich habe eine mögliche Lösung erstellt, wie man abstrakte “Klassen” erstellt und die Erstellung von Objekten dieses Typs blockiert.

//our Abstract class
var Animal=function(){
  
    this.name="Animal";
    this.fullname=this.name;
    
    //check if we have abstract paramater in prototype
    if (Object.getPrototypeOf(this).hasOwnProperty("abstract")){
    
    throw new Error("Can't instantiate abstract class!");
    
    
    }
    

};

//very important - Animal prototype has property abstract
Animal.prototype.abstract=true;

Animal.prototype.hello=function(){

   console.log("Hello from "+this.name);
};

Animal.prototype.fullHello=function(){

   console.log("Hello from "+this.fullname);
};

//first inheritans
var Cat=function(){

	  Animal.call(this);//run constructor of animal
    
    this.name="Cat";
    
    this.fullname=this.fullname+" - "+this.name;

};

Cat.prototype=Object.create(Animal.prototype);

//second inheritans
var Tiger=function(){

    Cat.call(this);//run constructor of animal
    
    this.name="Tiger";
    
    this.fullname=this.fullname+" - "+this.name;
    
};

Tiger.prototype=Object.create(Cat.prototype);

//cat can be used
console.log("WE CREATE CAT:");
var cat=new Cat();
cat.hello();
cat.fullHello();

//tiger can be used

console.log("WE CREATE TIGER:");
var tiger=new Tiger();
tiger.hello();
tiger.fullHello();


console.log("WE CREATE ANIMAL ( IT IS ABSTRACT ):");
//animal is abstract, cannot be used - see error in console
var animal=new Animal();
animal=animal.fullHello();

Wie Sie sehen können, gibt uns das letzte Objekt einen Fehler, weil das Tier im Prototyp eine Eigenschaft hat abstract. Allerdings ist es Animal nicht etwas was hat Animal.prototype In der Prototypkette mache ich:

Object.getPrototypeOf(this).hasOwnProperty("abstract")

Also überprüfe ich, ob mein nächstgelegenes Prototyp-Objekt vorhanden ist abstract Eigenschaft, nur Objekt, das direkt aus erstellt wurde Animal Prototyp hat diese Bedingung auf wahr. Funktion hasOwnProperty überprüft nur die Eigenschaften des aktuellen Objekts, nicht seine Prototypen, so dass wir zu 100% sicher sind, dass die Eigenschaft hier nicht in der Prototypenkette deklariert ist.

Jedes Objekt, das von Object abstammt, erbt die hatEigeneEigenschaft Methode. Diese Methode kann verwendet werden, um zu bestimmen, ob ein Objekt die angegebene Eigenschaft als direkte Eigenschaft dieses Objekts hat; Im Gegensatz zum in-Operator überprüft diese Methode nicht die Prototypkette des Objekts. Mehr dazu:

An meinem Vorschlag müssen wir nichts ändern constructor jedes Mal danach Object.create wie es in der aktuell besten Antwort von @Jordão ist.

Die Lösung ermöglicht es auch, viele abstrakte Klassen in einer Hierarchie zu erstellen, wir müssen nur erstellen abstract Eigenschaft im Prototyp.

1257720cookie-checkWie erstelle ich eine abstrakte Basisklasse in JavaScript?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy