JavaScript-Erweiterungsklasse

76

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?

Lucas Penney
quelle
Mögliches Duplikat von Wie erbe ich von einer Klasse in Javascript?
Ciro Santilli 法轮功 冠状 病 六四 事件 20

Antworten:

147

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:

Browserunterstützung für Object.create

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 vorzuziehen new 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 :

ES6-Browserunterstützung

Oliver Spryn
quelle
3
Was ist, wenn der Personenkonstruktor Parameter benötigt, z. B. Person (Name) ...?
Phung D. An
2
@PhamHuyAnh Mach einfach so etwas wiefunction Person(n) { this.name = n; }
Oliver Spryn
Student.prototype = new Person();Diese Zeile führt zu einem Fehler, wenn ich auf den Parameter in der Superklasse zugreife.
Max
1
Student.prototype = Object.create(Person.prototype);Hier funktioniert die klassische Erstellungsmethode
Max
1
Ich versuche, eine Erweiterungsfunktion zu erstellen, die alles von selbst erledigt. Ist es möglich, "Person.call (this)" irgendwie zu verschieben? oder aus dem folgenden Beispiel "Monster.apply (dies, Argumente);" zu einer solchen Funktion? Ich habe Probleme damit.
sebastian_t
18

ES6 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
        }

}
Abdennour TOUMI
quelle
2
Dies ist so viel sauberer, kann die Arbeit in Chrome
51.0
1
Verwenden Sie try{}catch(e){}Blöcke, um dies zu verwalten, und weisen Sie den Endbenutzer an, seinen Browser zu aktualisieren, falls dies erforderlich ist.
Abdennour TOUMI
10

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 extendsein reserviertes Wort in JS ist und Sie möglicherweise Warnungen erhalten, wenn Sie Ihren Code fusseln. Sie können ihn einfach benennen inherits, das ist, was ich normalerweise mache.

Mit diesem Helfer und der Verwendung eines Objekts propsals 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"
elclanrs
quelle
Wenn Sie dies verwenden, Monkeywerden die MonsterEigenschaften ( health) nicht geerbt . Sie erhalten auch "ReferenceError: Monkey is not defined", wenn Monkeydies vor dem Aufruf nicht definiert wurdeMonkey.extends(Monster)
Phil
@Phil, es ist eine Funktionsdeklaration, die angehoben wird, es sollte funktionieren. Das einzige "Problem", das Sie durch diesen Code erhalten, ist "Erweitert ist ein reserviertes Wort", aber Sie können es leicht in eine andere Kennung ändern.
Elclanrs
1
Danke Kumpel. Das ist toll. Könnte es in Node.js anwenden, um eine Basisklasse für Konstruktoren usw. zu erstellen, so dass ich nicht jedes Mal Mongo-Verbindungen usw. erstellen muss, wenn ich eine Konstruktklasse erstelle
Mattijs
6

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

Harold
quelle
Danke für den Link zu dieser Bibliothek !! Es sieht toll aus: D Scheint so, als hätte ich so lange gesucht! <3
x3ro
1

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
Yarik
quelle
1

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();
        }
    }
);
GMeister
quelle
0

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');
    }
  }])
Daniel Garmoshka
quelle
0

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
    }
}
Steffomio
quelle
0

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 2015Klassen ist nichts anderes als syntaktischer Zucker über das prototypische Vererbungsmuster in Javascript. Hier das erste Protokoll, in dem wir auswerten typeof 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 wie ReactJSund verwendet Angular2+.

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.

Willem van der Veen
quelle
0

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

gdanov
quelle