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のハーモニー
ES2015(ES6)クラスのルールは、基本的に次のとおりです。
this
を呼び出すまでsuper
を使用できません。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
を初期化する場合)、または明示的な置換オブジェクトが返されない場合、コンストラクター呼び出しの最終行は例外をスローします。
super
はがconstructor
内の最初の行でなければならないことを示す複数の回答とコメントがありました。それは単に間違っています。 @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());
新しい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.
この解決策を投稿するために登録したばかりです。なぜなら、実際にはこれを回避する簡単な方法があるからです。 スーパーコンストラクターのみを使用しながら、サブメソッドのロジックを上書きするようにクラス作成パターンを調整しますそして、コンストラクター引数をそれに転送します。
サブクラス自体にコンストラクタを作成するのではなく、それぞれのサブクラスでオーバーライドされるメソッドへの参照のみを作成します。
つまり、ユーザーに強制されるコンストラクター機能から解放し、通常のメソッドを控えることを意味します-オーバーライドでき、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");
乾杯!
サブクラスでコンストラクターを完全に省略する場合、サブクラスで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 をお読みください。
次の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);
});
試してください:
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();
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()
を使用します。
このソリューションでは、インスタンス化ごとに追加のメソッドにステップインする必要があるため、デバッグが煩わしいことに注意してください。
簡単な解決策:説明の必要がないことは明らかだと思います。
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
}
}