web-dev-qa-db-de.com

Wie kann man eine Klasse erweitern, ohne Super in ES6 verwenden zu müssen?

Ist es möglich, eine Klasse in ES6 zu erweitern, ohne die super-Methode aufzurufen, um die übergeordnete Klasse aufzurufen?

EDIT: Die Frage könnte irreführend sein. Ist es der Standard, den wir super() nennen müssen, oder fehlt mir etwas?

Zum Beispiel:

class Character {
   constructor(){
      console.log('invoke character');
   }
}

class Hero extends Character{
  constructor(){
      super(); // exception thrown here when not called
      console.log('invoke hero');
  }
}

var hero = new Hero();

Wenn ich nicht super() für die abgeleitete Klasse anrufe, erhalte ich ein Bereichsproblem -> this is not defined

Ich führe dies mit iojs --harmony in v2.3.0 aus

74
xhallix

Die Regeln für ES2015 (ES6) -Klassen ergeben sich im Wesentlichen aus:

  1. In einem untergeordneten Klassenkonstruktor kann this erst verwendet werden, wenn super aufgerufen wird.
  2. ES6-Klassenkonstruktoren MÜSSEN super aufrufen, wenn es sich um Unterklassen handelt, oder sie müssen ein Objekt explizit zurückgeben, um die Stelle des nicht initialisierten zu ersetzen.

Dies betrifft zwei wichtige Abschnitte der ES2015-Spezifikation.

Abschnitt 8.1.1.3.4 definiert die Logik, um zu entscheiden, was this in der Funktion ist. Für Klassen ist es wichtig, dass es möglich ist, dass this in einem "uninitialized"-Status ist. Wenn Sie in diesem Status versuchen, this zu verwenden, wird eine Ausnahme ausgelöst.

Abschnitt 9.2.2 , [[Construct]], der das Verhalten von Funktionen definiert, die über new oder super aufgerufen werden. Beim Aufruf eines Basisklassenkonstruktors wird this in Schritt # 8 von [[Construct]] initialisiert. In allen anderen Fällen ist this jedoch nicht initialisiert. Am Ende der Konstruktion wird GetThisBinding aufgerufen. Wenn super noch nicht aufgerufen wurde (wodurch this initialisiert wird) oder ein explizites Ersetzungsobjekt nicht zurückgegeben wurde, wird in der letzten Zeile des Konstruktoraufrufs eine Ausnahme ausgelöst.

127
loganfsmyth

Es gab mehrere Antworten und Kommentare, die besagten, dass super [~ # ~] [~ # ~] muss = Sei die erste Zeile in constructor. Das ist einfach falsch @loganfsmyth Antwort hat die erforderlichen Referenzen der Anforderungen, aber es läuft auf Folgendes hinaus:

Erben des (extends) -Konstruktors muss Rufen Sie super auf, bevor Sie this verwenden und bevor Sie zurückgeben, auch wenn this wird nicht verwendet

Siehe Fragment unten (funktioniert in Chrome ...), um herauszufinden, warum es möglicherweise sinnvoll ist, Anweisungen (ohne Verwendung von this) zu haben, bevor super aufgerufen wird.

'use strict';
var id = 1;
function idgen() {
  return 'ID:' + id++;
}

class Base {
  constructor(id) {
    this.id = id;
  }

  toString() { return JSON.stringify(this); }
}

class Derived1 extends Base {
  constructor() {
    var anID = idgen() + ':Derived1';
    super(anID);
    this.derivedProp = this.baseProp * 2;
  }
}

alert(new Derived1());
10
Amit

Die neue es6-Klassensyntax ist nur eine andere Notation für "alte" es5-Klassen "mit Prototypen. Daher können Sie eine bestimmte Klasse nicht instanziieren, ohne ihren Prototyp (die Basisklasse) festzulegen. 

Das ist, als würde man Käse auf sein Sandwich legen, ohne es zu machen. Sie können auch nicht Käse vorher das Sandwich machen, also ...

Das Verwenden des this-Schlüsselworts vor dem Aufruf der Superklasse mit super() ist ebenfalls nicht zulässig.

// valid: Add cheese after making the sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        super();
        this.supplement = "Cheese";
    }
}

// invalid: Add cheese before making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
        super();
    }
}

// invalid: Add cheese without making sandwich
class CheeseSandwich extend Sandwich {
    constructor() {
        this.supplement = "Cheese";
    }
}

Wenn Sie keinen Konstruktor für eine Basisklasse angeben, wird die folgende Definition verwendet: 

constructor() {}

Für abgeleitete Klassen wird der folgende Standardkonstruktor verwendet:

constructor(...args) {
    super(...args);
}

BEARBEITEN: Gefunden auf developer.mozilla.org:

When used in a constructor, the super keyword appears alone and must be used before the this keyword can be used.

Quelle

6
marcel

Sie müssen sich nur für diese Lösung registrieren, da die Antworten mich hier nicht am wenigsten zufriedenstellen, da es sich eigentlich um einen einfachen Weg handelt. Passen Sie Ihr Klassenerstellungsmuster an, um Ihre Logik in einer Untermethode zu überschreiben, während Sie nur den Superkonstruktor verwenden und leiten Sie die Konstruktorargumente an ihn weiter.

Wie in Ihnen erstellen Sie in Ihren Unterklassen keinen Konstruktor per se, sondern beziehen sich nur auf eine Methode, die in der jeweiligen Unterklasse überschrieben wird. 

Das bedeutet, dass du dich von der Konstruktorfunktionalität befreit hast, die auf dich erzwungen wird, und eine reguläre Methode unterlassen - die überschrieben werden kann und nicht zwingt (), wenn du dir die Wahl lässt, ob, wo und wie du möchtest Super anrufen (voll optional) zB:

super.ObjectConstructor(...)

class Observable {
  constructor() {
    return this.ObjectConstructor(arguments);
  }

  ObjectConstructor(defaultValue, options) {
    this.obj = { type: "Observable" };
    console.log("Observable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class ArrayObservable extends Observable {
  ObjectConstructor(defaultValue, options, someMoreOptions) {
    this.obj = { type: "ArrayObservable" };
    console.log("ArrayObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

class DomainObservable extends ArrayObservable {
  ObjectConstructor(defaultValue, domainName, options, dependent1, dependent2) {
    this.obj = super.ObjectConstructor(defaultValue, options);
    console.log("DomainObservable ObjectConstructor called with arguments: ", arguments);
    console.log("obj is:", this.obj);
    return this.obj;
  }
}

var myBasicObservable = new Observable("Basic Value", "Basic Options");
var myArrayObservable = new ArrayObservable("Array Value", "Array Options", "Some More Array Options");
var myDomainObservable = new DomainObservable("Domain Value", "Domain Name", "Domain Options", "Dependency A", "Depenency B");

prost! 

4
justyourimage

Sie können super () in Ihrer Unterklasse weglassen, wenn Sie den Konstruktor in Ihrer Unterklasse vollständig weglassen. Ein verborgener Standardkonstruktor wird automatisch in Ihre Unterklasse aufgenommen. Wenn Sie jedoch den Konstruktor in Ihre Unterklasse aufnehmen, muss super () in diesem Konstruktor aufgerufen werden.

class A{
   constructor(){
      this.name = 'hello';   
   }
}
class B extends A{
   constructor(){
      // console.log(this.name); // ReferenceError
      super();
      console.log(this.name);
   }
}
class C extends B{}  // see? no super(). no constructor()

var x = new B; // hello
var y = new C; // hello

Lesen Sie dies für weitere Informationen.

4
Chong Lip Phang

Ich würde empfehlen, OODK-JS zu verwenden, wenn Sie folgende OOP Konzepte entwickeln möchten.

OODK(function($, _){

var Character  = $.class(function ($, µ, _){

   $.public(function __initialize(){
      $.log('invoke character');
   });
});

var Hero = $.extends(Character).class(function ($, µ, _){

  $.public(function __initialize(){
      $.super.__initialize();
      $.log('invoke hero');
  });
});

var hero = $.new(Hero);
});
1
OBDM

Versuchen:

class Character {
   constructor(){
     if(Object.getPrototypeOf(this) === Character.prototype){
       console.log('invoke character');
     }
   }
}


class Hero extends Character{
  constructor(){
      super(); // throws exception when not called
      console.log('invoke hero');
  }
}
var hero = new Hero();

console.log('now let\'s invoke Character');
var char = new Character();

Demo

0
Jonathan de M.

Die Antwort von justyourimage ist der einfachste Weg, aber sein Beispiel ist ein wenig aufgebläht. Hier ist die generische Version:

class Base {
    constructor(){
        return this._constructor(...arguments);
    }

    _constructor(){
        // just use this as the constructor, no super() restrictions
    }
}

class Ext extends Base {
    _constructor(){ // _constructor is automatically called, like the real constructor
        this.is = "easy"; // no need to call super();
    }
}

Erweitern Sie nicht die eigentliche constructor(), sondern verwenden Sie einfach die gefälschte _constructor() für die Instantiierungslogik.

Beachten Sie, dass diese Lösung das Debuggen lästig macht, da Sie für jede Instantiierung eine zusätzliche Methode verwenden müssen.

0
Michael Lewis

Einfache Lösung: Ich denke, es ist keine Erklärung erforderlich.

class ParentClass() {
    constructor(skipConstructor = false) { // default value is false
        if(skipConstructor) return;
        // code here only gets executed when 'super()' is called with false
    }
}
class SubClass extends ParentClass {
    constructor() {
        super(true) // true for skipping ParentClass's constructor.
        // code
    }
}