web-dev-qa-db-fra.com

Comment étendre une classe sans avoir à utiliser super dans ES6?

Est-il possible d'étendre une classe dans ES6 sans appeler la méthode super pour appeler la classe parent?

EDIT: La question pourrait être trompeuse. Est-ce la norme que nous devons appeler super() ou est-ce que je manque quelque chose?

Par exemple:

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();

Lorsque je n'appelle pas super() sur la classe dérivée, le problème de portée survient -> this is not defined

J'exécute ceci avec iojs --harmony dans la v2.3.0

74
xhallix

Les règles pour les classes ES2015 (ES6) se résument comme suit:

  1. Dans un constructeur de classe enfant, this ne peut pas être utilisé tant que super n'est pas appelé.
  2. Les constructeurs de classe ES6 DOIVENT appeler super s'ils sont des sous-classes, ou ils doivent explicitement renvoyer un objet à la place de celui qui n'a pas été initialisé.

Cela se résume à deux sections importantes de la spécification ES2015.

La section 8.1.1.3.4 définit la logique permettant de décider de ce que this contient dans la fonction. L'important pour les classes est qu'il est possible que this soit dans un état "uninitialized" et que, lorsqu'il est dans cet état, toute tentative d'utilisation de this lève une exception.

La section 9.2.2 , [[Construct]], qui définit le comportement des fonctions appelées via new ou super. Lors de l'appel d'un constructeur de classe de base, this est initialisé à l'étape 8 de [[Construct]], mais pour tous les autres cas, this est non initialisé. GetThisBinding est appelé à la fin de la construction. Par conséquent, si super n'a pas encore été appelé (initialisant ainsi this) ou si un objet de remplacement explicite n'a pas été renvoyé, la dernière ligne de l'appel du constructeur lève une exception.

127
loganfsmyth

Il y a eu plusieurs réponses et commentaires indiquant que superDOIT _ être la première ligne de constructor. C'est tout simplement faux. @loganfsmyth answer a les références requises des exigences, mais cela se résume à:

Constructeur hérité (extends) doit appeler super avant d'utiliser this et avant de revenir même si this n'est pas utilisé

Voir le fragment ci-dessous (fonctionne dans Chrome ...) pour voir pourquoi il peut être judicieux d'avoir des instructions (sans utiliser this) avant d'appeler super.

'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());

9
Amit

La nouvelle syntaxe de classe es6 n'est qu'une autre notation pour les "anciennes" classes "es5" avec des prototypes. Par conséquent, vous ne pouvez pas instancier une classe spécifique sans définir son prototype (la classe de base). 

C'est comme mettre du fromage sur votre sandwich sans le préparer. Aussi, vous ne pouvez pas mettre du fromage avant pour faire le sandwich, alors ...

... utiliser le mot clé this avant d'appeler la super-classe avec super() n'est également pas autorisé.

// 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";
    }
}

Si vous ne spécifiez pas de constructeur pour une classe de base, la définition suivante est utilisée: 

constructor() {}

Pour les classes dérivées, le constructeur par défaut suivant est utilisé:

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

EDIT: trouvé ceci sur developer.mozilla.org:

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

La source

6
marcel

Je viens juste de m'inscrire pour poster cette solution car les réponses ici ne me satisfont pas du tout, car il existe en fait un moyen simple de contourner ce problème. Ajustez votre modèle de création de classe pour écraser votre logique dans une sous-méthode en n'utilisant que le super constructeur et transmettez-lui les arguments du constructeur.

Comme dans le cas, vous ne créez pas de constructeur dans vos sous-classes en tant que telles, mais faites uniquement référence à une méthode qui est substituée dans la sous-classe correspondante. 

Cela signifie que vous vous libérez des fonctionnalités du constructeur qui vous sont imposées et que vous vous abstenez d'adopter une méthode régulière - qui peut être remplacée et ne vous impose pas super () en vous laissant le choix si, où et comment appel super (entièrement optionnel) par exemple:

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");

à votre santé! 

4
justyourimage

Vous pouvez omettre super () dans votre sous-classe, si vous omettez complètement le constructeur dans votre sous-classe. Un constructeur par défaut "masqué" sera automatiquement inclus dans votre sous-classe. Cependant, si vous incluez le constructeur dans votre sous-classe, super () doit être appelé dans ce constructeur.

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

Lisez this pour plus d'informations.

4
Chong Lip Phang

Je recommanderais d’utiliser OODK-JS si vous avez l’intention de développer les concepts suivants OOP.

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

Essayer:

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.

La réponse par votre image est la manière la plus simple, mais son exemple est un peu bouffi. Voici la version générique:

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

Ne prolongez pas le constructor() réel, utilisez simplement le faux _constructor() pour la logique d’instanciation.

Notez que cette solution rend le débogage ennuyeux car vous devez utiliser une méthode supplémentaire pour chaque instanciation.

0
Michael Lewis

Solution simple: je pense que son clair pas besoin d'explication.

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