ES6によるclass構文の概要
javascriptにおけるclass構文は、主に「クラスの拡張」に重きを置いたものであるため、構文自体は他の言語を模倣した形となっている。つまり、「インスタンス」という概念はあるものの、いわゆる「クラス」がなく、「プロトタイプという概念」だけが存在するということである。ES6でのclassキーワードでのクラスは、関数として呼び出しは出来ないが、内部的には関数である。
class User {
static age = 100
constructor(name = '') {
this.name = name
}
get() {
/*コード内容*/
}
set() {
/*コード内容*/
}
}
//インスタンスの作成
const sample_user = new User('Tanaka')
特徴的なのは、constructor(Cではコンストラクタ)である。constructorはインスタンスが作成されたときに呼び出されるため、constructor内のthisはそのインスタンスを指している。constructorメソッドはオプションであり、クラスのインスタンスに対して、初期設定を行う。
またインスタンスの作成方法は、newキーワードを使う。
newキーワードで生成したインスタンスに関して後からインスタンスを追加することもでき、このことから他言語のクラスとは違うことがわかる。
classにおけるthisとbind
thisキーワードは、コンストラクターによって生成されるインスタンスを表すものである。thisキーワードはどこからでも参照できる特別な変数であるがよえに、呼び出す場所、呼び出しの方法によって中身が変わってきてしまう。
そして、class内のメソッドは、自動的にバインドがされない。バインドされないとは、あるメソッドを呼び出したときにそれがどこのclassで定義されているメソッドなのかがわからないという仕様である。例えば、
class app {
get() {
const nameList = /*urlからnameのリストをダウンロードする*/
return nameList.map(function(name) {
return this.set(name)
})
}
set(name) {
this.name = name + '様'
}
}
一見よさそうに見えるが、getメソッドが呼び出したsetメソッド内のthisはもうそのインスタンスを指していないため、nameは正しく保存されない。
このことを解決する手段は何種類かある。
一つ目は、constructorでバインドしてしまうことだ。
class app {
constructor(name = '') {
this.name = name
this.set = this.set.bind(this)
}
get() {
const nameList = /*urlからnameのリストをダウンロードする*/
return nameList.map(function(name) {
return this.set(name)
})
}
set(name) {
this.name = name + '様'
}
}
constructorでbind(this)を用いることで、setを呼び出したインスタンスとset内で用いられているthisを同じインスタンスにしている。C言語でいうところのポインタを同じにするということだ。
二つ目はthatをかませることだ。
class app {
constructor(name = ''){
this.name = name
}
get() {
const that = this
const nameList = /*urlからnameのリストをダウンロードする*/
return nameList.map(function(name) {
return that.set(name)
})
}
set(name) {
this.name = name + '様'
}
}
もしくは
get() {
const nameList = /*urlからnameのリストをダウンロードする*/
return nameList.map(function(name) {
return this.set(name)
}, this)
}
thatをいちいち作るとかっこ悪いのでmapの二つ目の変数にオプションとして付け加えるという方法もある。
しかし、一番使われているのはアロー関数を用いる解決策ではないだろうか。
アロー関数のthisはアロー関数が定義されたthisにバインドされる。
get() {
const nameList = /*urlからnameのリストをダウンロードする*/
return nameList.map((name) => this.set(name))
}
このthisはそれぞれのnameに対して適切に行われる。
classの拡張
javascriptでclassを継承するときに覚えておくべきことは、やはりclassは親のプロパティを継承することである
class Student extends Person {
constructor(name, age, score) {
super(name, age)
this.score = score
}
}
javascriptではentendsキーワードを用いて、継承を行う。superキーワードを使うことで、いちいち親のコンストラクタをもう一度呼び出さなくてもよい。また、superキーワードは親のメソッドを呼び出したいときにも使われる。
参考資料
山田祥寛様 「javascript本格入門」
JD Isaacks様 「入門javaScriptプログラミング」