web-dev-qa-db-ja.com

ES6でスーパーを使用せずにクラスを拡張する方法は?

superメソッドを呼び出して親クラスを呼び出すことなく、ES6でクラスを拡張することは可能ですか?

編集:質問は誤解を招くかもしれません。それはsuper()を呼び出さなければならない標準ですか、それとも何か不足していますか?

例えば:

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

派生クラスでsuper()を呼び出さないと、スコープの問題が発生します-> this is not defined

これをiojsで実行しています-v2.3.0のハーモニー

83
xhallix

ES2015(ES6)クラスのルールは、基本的に次のとおりです。

  1. 子クラスコンストラクターでは、thisを呼び出すまでsuperを使用できません。
  2. ES6クラスコンストラクターは、サブクラスである場合はsuperを呼び出す必要があります。または、初期化されていないオブジェクトの代わりにオブジェクトを明示的に返す必要があります。

これは、ES2015仕様の2つの重要なセクションに分類されます。

セクション 8.1.1.3.4 は、関数内のthisを決定するロジックを定義します。クラスの重要な部分は、this"uninitialized"状態になる可能性があり、この状態のときにthisを使用しようとすると例外がスローされることです。

セクション 9.2.2[[Construct]]。これは、newまたはsuperを介して呼び出される関数の動作を定義します。基本クラスコンストラクターを呼び出す場合、this[[Construct]]のステップ#8で初期化されますが、他のすべての場合、thisは初期化されません。構築の最後でGetThisBindingが呼び出されます。したがって、superがまだ呼び出されていない場合(したがってthisを初期化する場合)、または明示的な置換オブジェクトが返されない場合、コンストラクター呼び出しの最終行は例外をスローします。

132
loganfsmyth

superconstructor内の最初の行でなければならないことを示す複数の回答とコメントがありました。それは単に間違っています。 @loganfsmythの回答には、要件の必須参照がありますが、要約すると次のようになります。

extends)コンストラクターの継承必須superを使用する前、およびthisが使用されていない場合でも返す前にthisを呼び出す

以下のフラグメント(Chromeで動作します...)を参照して、thisを呼び出す前に(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());
10
Amit

新しいes6クラス構文は、プロトタイプを使用した「古い」es5「クラス」のその他の表記法にすぎません。したがって、プロトタイプ(基本クラス)を設定せずに特定のクラスをインスタンス化することはできません。

それは、サンドイッチを作らずにサンドイッチにチーズを入れるようなものです。また、サンドイッチを作る前にチーズを入れられないので...

... super()でスーパークラスを呼び出す前にthisキーワードを使用することも許可されていません。

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

基本クラスのコンストラクターを指定しない場合、次の定義が使用されます。

constructor() {}

派生クラスの場合、次のデフォルトコンストラクターが使用されます。

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

編集:developer.mozilla.orgでこれを見つけました:

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

ソース

6
marcel

この解決策を投稿するために登録したばかりです。なぜなら、実際にはこれを回避する簡単な方法があるからです。 スーパーコンストラクターのみを使用しながら、サブメソッドのロジックを上書きするようにクラス作成パターンを調整しますそして、コンストラクター引数をそれに転送します。

サブクラス自体にコンストラクタを作成するのではなく、それぞれのサブクラスでオーバーライドされるメソッドへの参照のみを作成します。

つまり、ユーザーに強制されるコンストラクター機能から解放し、通常のメソッドを控えることを意味します-オーバーライドでき、super()を強制することはありません。スーパー(完全にオプション)を呼び出したい。例:

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

乾杯!

4
justyourimage

サブクラスでコンストラクターを完全に省略する場合、サブクラスでsuper()を省略できます。 「非表示」のデフォルトコンストラクターは、サブクラスに自動的に含まれます。ただし、サブクラスにコンストラクターを含める場合は、そのコンストラクターでsuper()を呼び出す必要があります。

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

詳細については、 this をお読みください。

4
Chong Lip Phang

次のOOP概念を開発する場合は、 OODK-JS を使用することをお勧めします。

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

試してください:

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.

Justyourimageによる答えが最も簡単な方法ですが、彼の例は少し肥大化しています。一般的なバージョンは次のとおりです。

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

実際のconstructor()を拡張せず、インスタンス化ロジックに偽の_constructor()を使用します。

このソリューションでは、インスタンス化ごとに追加のメソッドにステップインする必要があるため、デバッグが煩わしいことに注意してください。

0
Michael Lewis

簡単な解決策:説明の必要がないことは明らかだと思います。

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