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
Les règles pour les classes ES2015 (ES6) se résument comme suit:
this
ne peut pas être utilisé tant que super
n'est pas appelé.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.
Il y a eu plusieurs réponses et commentaires indiquant que super
DOIT _ ê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());
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.
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é!
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.
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);
});
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();
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.
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
}
}