JavaScriptにもクラスがやってきた!JavaScriptの新しいclass構文をマスターしよう

ECMAScript 2015(ECMAScript 6)で新たに追加された待望のclass構文について、その概要をサンプルコードを交えて紹介します。

これまでのJavaScriptにおけるクラス

多くのプログラミング言語はクラスを作れる機能を持っていますが、JavaScriptにその機能は用意されていませんでした。しかし、JavaScriptにはprototypeという柔軟な仕組みが存在しており、このprototypeを利用することで、他の言語で表現されている「クラス」と似たような振る舞いを再現することが可能でした。

それは例えば、こんなふうにです。

コンストラクタであるfunction Catを宣言し、そのprototypeにインスタンスメソッド群を定義したオブジェクトを指定します。すると、new Catした時、作成されたオブジェクトのプロパティは、そのオブジェクト自身に直接定義されていない場合、Cat.prototypeに定義されている同名のプロパティを参照するという具合です。

このように、単純なオブジェクトの雛形を作るだけならまだよいのですが、あるクラスを継承し、別のクラスを作る、いわゆる「クラスの継承」を表現するのはちょっと複雑です。その実装方法についてはここでは割愛しますが、例えばこのCatであれば、より上位の概念を表現したクラスAnimalを用意し、CatAnimalを継承して作られる……というような場合です。複雑と言っても、よく使われる概念ではあるので、これまでは多くのライブラリやトランスパイラがこの機能を担っていました。

このような、prototypeを利用した「クラス」的な振る舞いの実装は、JavaScript初学者のひとつのハードルになっていたと言ってもよいのではないでしょうか。

ES6のclass構文

そんな「クラス」を実現するclass構文が、ES6では用意されました。

class構文を使えば、クラスの継承も簡単に行えます。その仕様の実態は、prototypeベースの継承であり、単なる糖衣構文に過ぎませんが、複雑な実装をすることなく誰でも簡単にクラスを扱えるようになったことは大きな意味があると言ってよいでしょう。

先ほどのCatを、ES6のclass構文で書くと、以下のようになります。

これで先ほどと同様の実行結果になります。

※ ここで使っているsetはsetter、getはgetterとしてES5で定義されている構文、walkメソッドを定義しているのはMethod definitionで、ES6で定義されている構文です。class構文とはまた別なので注意して下さい。

静的メソッド

class構文は、静的メソッド※の定義方法も用意しています。先ほどのCatに、作成した猫の数を返すcountメソッドを実装してみたのが以下です。

※クラスのインスタンスを作らずとも呼べるメソッドのことを言います。

Catconstructorが実行されると猫の匹数を示すcatCountがインクリメントされ、静的メソッドのcount()が呼ばれると、このcatCountを返します。

※ ちなみに、メソッド以外の静的なプロパティを設定すること(ここだと例えばCat.catCountに猫の数を格納するようなこと)は、class構文には今のところ用意されていないようです。これについては2015年10月現在、proposalが出ているだけの状態で、今後どうなるかはまだ分かりません。

クラスの継承

クラスを継承し、別のクラスを作るには、extendsを使います。以下は、Animalを継承したCatクラスを定義した例です。

Catのインスタンスオブジェクトであるcat1cat2は、Catに定義されているメソッドが利用できるだけではなく、Animalに定義されているconstructorで初期化され、Animalで定義されているメソッドを利用できていることが分かります。

継承元のメソッド呼び出し

クラスを継承した場合、継承元のメソッドを呼びたいことがあります。その場合、以下のようにsuperを使います。

constructor内で、super(name)と、Animalconstructorを実行し、walk()内でsuper.walk()と、Animalwalkを実行しています。

class構文を使わない場合、Animal.prototype.walk.apply(this, arguments)などと、継承元のメソッドをprototype経由で直接参照し、applyでコンテキストを自分にして実行するなどというような実装が必要です。superを使えば、同じ内容をとてもシンプルに書くことができます。

その他注意点

他、筆者がclass構文を使ってみて気になったこととして、インスタンスのプロパティをprototype直下に直接指定できないという点が気になりました。例えば、はじめに挙げたCatでインスタンス作成時にnameを受け取らない場合、以下のように書けば、setNameする前にgetNameが呼ばれた場合、'名無しの猫'が名前として返ります。

しかし、ES6のclass構文では、class宣言時に設定できるのはメソッドのみです。上記例のように_nameprototypeに指定することができません。インスタンスのプロパティの初期値を設定したい場合、constructor内で設定する必要があります。

prototype直下にオブジェクトを指定した場合、全てのインスタンスで同一のオブジェクトを参照してしまい、思わぬバグの原因になったりすることもありえるため、このような書き方をそもそもしないほうがよいという見解もあります。

ブラウザ対応状況

class構文の対応状況は、2015年10月現在、まだまだといった状況です。

筆者が確認したタイミングで対応しているのは、Safari9 (Webkit)だけです。デベロッパー版や設定フラグなど考慮すると Chrome45 (enable-javascript-harmonyをオンにし、strict modeのみで動作)、Firefox nightly (v44)、Microsoft Edge(experimental featureをオンにして使用)などで利用できます。一般的な閲覧環境を想定する場合、class構文を使用するのはまだ先だと考えておいてよいでしょう。bableやtraceur等のトランスパイラは対応しています。ただし、まだclass構文の全ての機能に対応しているわけではないようです。

ちなみに筆者は、本稿を書くためのサンプルを、Chrome canaryで動作することを確認しています。

まとめ

以上、ES6のclass構文について、その概要を簡単に紹介しました。筆者は、好きでよくCoffeeScriptを使っていたのですが、CoffeeScriptを使う大きな理由の一つとして、CoffeeScript独自のclass構文が使えるようになっているというところがありました。そういったコードの設計の根幹部分が言語として用意された状態だと、コードの見通しがよくなるなぁと感じていまして。しかし、ES6にてclass構文が実装されたとなれば、自分がCoffeeScriptを選択する理由が一つ、なくなった感じがします。

ほか、class構文を使って書かれたコードは、そのコードで表現したいことが、同様の内容をclass構文を使わないで書いた場合よりも、よりシンプルに表現できているように感じます。これは、チーム間でのコミュニケーション効率を高め、開発をスムーズにするかもしれません。

class構文がやってくれることは、これまでJavaScriptで書かれてきたいわゆる「クラス」的なものの実装そのものであるため、class構文を使ったからといって、これまで書いてきたJavaScriptと根本的に設計が変わってしまうということはないでしょう。ブラウザの対応はまだまだのようですが、babelをお供に、開発に採用できるケースはかなり多いのではないかと思います。

de:code 2017
Powered byNTT Communications

tag list

アクセシビリティ イベント エンタープライズ デザイン ハイブリッド パフォーマンス ブラウザ プログラミング マークアップ モバイル 海外 高速化 Angular2 AngularJS Canvas Chrome Cordova CSS de:code ECMAScript Edge Firefox Google Google I/O 2014 HTML5 Conference 2013 html5j IoT JavaScript Microsoft Node.js PhoneGap Polymer React Safari SkyWay TypeScript UI UX W3C W3C仕様 Webアプリ Web Components WebGL WebRTC WebSocket