読者です 読者をやめる 読者になる 読者になる

JS.next

JavaScriptの最新実装情報を追うブログ

superキーワードについて

★★★ ES2015 仕様紹介

概要

長らく予約語であった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(最新の仕様に合わせ修正)