superキーワードについて
概要
長らく予約語であったsuperキーワードが、継承元の関数を呼ぶためのキーワードとして機能するようになった。
2種類の使い方
superキーワードは場所によって2種類の使い方があり、それぞれ違う振る舞いをする。
1つ目はコンストラクタ内で使う場合、2つ目はメソッド内で使う場合である。
コンストラクタ内で使う場合
ここでいうコンストラクタとは、Class構文中のconstructorメソッドのことである。
この場合、「super()」や「new super」などとすることにより、その関数のプロトタイプである関数を呼ぶことができる。
そして、 呼び出し先が返した値が以後呼び出し元のthisとして使われる。
つまり、Animalコンストラクタを継承したCatコンストラクタを定義したい以下の例だと、
function Animal( name ) { this.name = name } function Cat( name ) { Animal.call( this, name ) // ※注目 } Cat.__proto__ = Animal var tama = new Cat('タマ') console.log( tama.name ) // "タマ"
を
class Animal { constructor( name ) { this.name = name } } class Cat extends Animal { constructor( name ) { super( name ) // ※注目 } } var tama = new Cat( 'タマ' ) console.log( tama.name ) // "タマ"
と書ける。
Catコンストラクタ内の「super()」はCatのプロトタイプであるAnimalを、thisを引き継いで呼ぶことになる。
メソッド内で使う場合
ここでいうメソッドとは、メソッド定義記法やClass構文中で定義されている関数のことである。
この場合、まずメソッドが定義される時に、各メソッドにそれが属するオブジェクトが『[[HomeObject]]』として紐付けされる。
属するオブジェクトとは、メソッド定義記法なら『定義されるオブジェクト』、Class構文中の一般メソッドであれば『クラスのprototypeオブジェクト』、Class構文中のstaticメソッドであれば『クラスオブジェクト』となる。
そしてメソッド内で「super.foo()」や「super['foo']()」などとすると、その[[HomeObject]]のプロトタイプの「foo」メソッドを呼ぶことができる。
その際、呼び出し先の「this」は呼び出し元の「this」に設定されて呼ばれる。(「new」を付けない場合)
つまり、Animalのprototypeオブジェクトを継承したCatのprototypeオブジェクトを定義したい以下のケースだと、
function Animal() { } Animal.prototype = { speak( cry ) { console.log( cry ) } } function Cat() { } Cat.prototype = { __proto__: Animal.prototype, meow() { Animal.prototype.speak.call( this, 'ミャオ' ) // ※注目 } } var tama = new Cat() tama.meow() // "ミャオ"
を
function Animal() { } Animal.prototype = { speak( cry ) { console.log( cry ) } } function Cat() { } Cat.prototype = { __proto__: Animal.prototype, meow() { super.speak( 'ミャオ' ) // ※注目 } } var tama = new Cat() tama.meow() // "ミャオ"
と書ける。
meowメソッド内の「super.speak()」はmeowの[[HomeObject]]であるCat.prototypeのプロトタイプであるAnimal.prototypeのspeakメソッドを、thisを引き継いで呼ぶことになる。
2種類を合わせると
Animalクラスを継承したCatクラスを定義したい以下の例だと、
function Animal( name ) { this.name = name } Animal.prototype = { speak( cry ) { console.log( this.name+ 'は' +cry+ 'と鳴きました' ) } } Cat.__proto__ = Animal function Cat( name ) { Animal.call( this, name ) // ※注目 } Cat.prototype = { __proto__: Animal.prototype, meow() { Animal.prototype.speak.call( this, 'ミャオ' ) // ※注目 } } var tama = new Cat( 'タマ' ) tama.meow() // "タマはミャオと鳴きました"
を
class Animal { constructor( name ) { this.name = name } speak( cry ) { console.log( this.name+ 'は' +cry+ 'と鳴きました' ) } } class Cat extends Animal { constructor( name ) { super( name ) // ※注目 } meow() { super.speak( 'ミャオ' ) // ※注目 } } var tama = new Cat( 'タマ' ) tama.meow() // "タマはミャオと鳴きました"
と書ける。
newTargetについて
実装されるバージョン
V8 ~3.30.18(ひと通りの振る舞い) 3.30.22(Class構文での[[HomeObject]]の自動設定) 3.31.2(オブジェクトリテラルでの[[HomeObject]]の自動設定) 4.x(最新の仕様に合わせ修正)