こんにちは、ライターのマサトです!
プログラミングって、書きあがったものを紐解いていくのもなかなか骨が折れますよね。
しかもなんかぐちゃぐちゃな書き方をされていると、どのような処理ができるのかがさっぱりわからないこともしばしば…
可能な限り、コードは美しくわかりやすく書きたいものです。
今回は、そんなわかりやすいコードの書き方の1つ、class構文について解説していきます!
下記の流れで、class構文をバッチリ使えるようになっちゃいましょう。
【基本】class構文とは?
【基本】class構文の使い方
【基本】class構文の活用
【発展】class構文の継承
【発展】privateなプロパティについて
【発展】クラスの継承
この記事で応用的な使い方までしっかり学習して、自分のスキルアップを目指しましょう!
class構文とは?
class構文はコンストラクタやメソッドを簡単に定義できるものです。
ES2015(ES6)から導入されたもので、旧来の書き方よりも簡単でわかりやすい書き方ができます。
一体どのようなものなのか、しっかり確認していきましょう。
JavaScriptにクラスはなかった
そもそも、JavaScriptにクラスという概念は存在していません。
一般体なオブジェクト指向言語がクラスベースというものなのに対して、JavaScriptはプロトタイプベースというものだからです。
JavaScriptでは、クラスという概念がない代わりに、function(関数)を使ってクラスのようなものを作って対応していました。
しかしこの書き方はゴチャゴチャしがちで、わかりづらいことが大きな欠点です。
そんな中、ES2015(ES6)という最新のJavaScriptで、class構文が導入されたのです。
これにより、クラスベースの言語に慣れた人でも、違和感なく扱えるようになりました。
つまり、JavaScript以外の言語に慣れ親しんだ人でもわかりやすく扱いやすいのが、class構文なのです。
どんな場面で使える?
class構文は、コンストラクタやメソッドの定義をする時に使われます。
コンストラクタはインスタンス(実体)を作成する関数、メソッドはオブジェクトの中に定義される関数ですね。
つまりclass構文は、色々な関数をひとまとめにして、使い回せるようにできるのです。
さらにclass構文は、他のclass構文に内容を引き継ぐことができます。
これにより、もともと書かれている構文をいじらなくても内容の追加などを行なうことができるのです。
class構文を使用することで、よりメンテナンス性の高いコードを書くことができるということですね!
コンストラクタやメソッドについてがよくわからない…
という方は、下記の記事もぜひ併せて読んでみてください。
>>コンストラクタとは?使い方まで徹底解説
>>普通の関数と何が違う?メソッドの全て
class構文の使い方
では、class構文の基本的な使い方を見ていきましょう。
構文や書き方、コンストラクタの作り方、メソッド定義について学んでいきます。
class構文の書き方
class構文の書き方にはクラス宣言とclass式の2種類があります。
それぞれ細かく見ていきましょう。
クラス宣言
クラス宣言で書く場合は、以下のようになります。
class クラス名 { //コンストラクタやメソッドを定義する }
class文の中にコンストラクタで初期設定を記述したり、各プロパティやメソッドを定義していくことになります。
この場合、クラス名は省略できないので必ず記述するようにしてください。
class式
class式による記述方法は以下のとおりです。
var myClass = class { //コンストラクタやメソッドを定義する }
この場合は、クラス名を省略することも可能です。
また、class式での特徴としてクラスを再宣言(再定義)することができるという点を覚えておきましょう。
コンストラクタを作る
次に、コンストラクタの作り方を見ていきましょう。
これは主にクラスの初期設定を行うわけですが、名前の通りconstructorというメソッドが用意されています。
次の例を見てください。
class User { constructor( name, age ) { this.name = name; this.age = age; } } //インスタンスを生成する var taro = new User('太郎', 32);
この例では、「name」「age」というプロパティをコンストラクタで定義しています。
このように記述することで、インスタンスを生成する際に引数として文字列や数値を受け取れるわけです。
class構文ではこのようにしてプロパティを定義します。
コンストラクタとインスタンスについてもっと詳しく知りたい方は、下記の記事も要チェックです。
メソッドを定義する
今度はメソッドをclass構文内に定義してみましょう!
例えば、getName()というメソッドを定義するには次のように記述します。
class User { constructor( name, age) { this.name = name; this.age = age; } getName() { return this.name; } } //インスタンスを生成する var taro = new User('太郎', 32); console.log(taro.getName());
実行結果
太郎
この例では、class構文内に直接getName()メソッドを定義しているのがわかりますね。
このようにメソッド名{ }という記述でいくつでもメソッドを定義していくことが可能です。
あとは、インスタンスを生成した際にメソッド名を記述するだけで処理を実行できます。
そもそもメソッドがよくわからない…
という方は、下記の記事も参考にしてみてください。
class構文の活用
この章では、class構文を使う上でさらに便利な使い方について見ていきましょう!
主に、ゲッター・セッターの使い方やprivate指定、静的メソッドの作り方を学んでいきます。
ゲッターメソッド(Getter)を作る
まずは、ゲッター(Getter)メソッドの作り方から見ていきましょう!
ゲッターメソッドを作ることで、クラスの特定のプロパティを取得することができます。
例えば、「name」というプロパティをゲッターメソッドで定義すると次のようになります。
class User { constructor( name, age ) { this.name = name; this.age = age; } //ゲッターメソッド get myName() { return this.name; } }
この例では、get構文を付与してmyName()というゲッターメソッドを定義しています。
クラス内の「name」プロパティを取得するには、this.nameと記述することでアクセスすることができます。
あとはreturnで返すようにすればゲッターメソッドの完成です。
使い方は通常のメソッドと同じく、下記のように記述します。
var taro = new User('太郎', 32); console.log( taro.myName );
このようにインスタンスを生成してから、直接「myName」を呼び出せばOKです。
セッターメソッド(Setter)を作る
次に、セッター(Setter)メソッドについて見ていきましょう!
ゲッターが読み取り専用なのに対して、セッターはプロパティに任意の値を設定するためのメソッドになります。
例えば、「name」プロパティに対してセッターメソッドを定義する場合は次のように記述します。
class User { constructor( name, age ) { this.name = name; this.age = age; } //セッターメソッド set myName( value ) { this.name = value; } // ゲッターメソッド get myName() { return this.name; } }
この例では、set構文を付与して「myName()」というセッターメソッドを定義しています。
プロパティに値を設定するので、引数にvalueを受けとるようにしています。
使い方は以下のとおりです!
var taro = new User('太郎', 32); taro.myName = '花子'; console.log( taro.myName );
実行結果
花子
インスタンスを生成してから「myName」に任意の値を代入することができます。
設定ができたかを確認するには、先ほど作成したゲッターメソッドを出力すると便利ですね。
実行結果を見ると、しっかりと「name」プロパティが変更されているのがわかりますね!
静的メソッド(static)を作る
今度は静的メソッド(static)について見ていきましょう!
静的メソッドの特徴は、インスタンスを生成しなくても利用できるという点です。
これを実現するのは簡単でstaticというキーワードを記述するだけになります。
次のサンプルを見てください!
class User { ・ ・ ・ static speak() { return 'みなさん、こんにちは'; } }
この例では、staticを付与することでspeak()という静的メソッドを定義しています。
このように記述するだけで、このメソッドはインスタンス不要で実行することができるのです。
console.log( User.speak() );
実行結果
みなさん、こんにちは
このようにクラス名に続けてspeak()を実行すればいいわけです。
privateなプロパティについて
この章では、プライベート(private)なプロパティについて見ていきましょう!
主に、パブリックなプロパティとSymbolによる固有のIDを使ったプロパティの作り方について学んでいきます。
パブリックなプロパティとは?
これまで基本的なクラスの作り方を見てきましたが、注意するべき点もいくつかあります。
特に、コンストラクタ内で定義したプロパティは「パブリック」なのでどこからでもアクセスできてしまうという点には注意が必要です。
例えば、次のようなクラスがあるとします。
class User1 { constructor(value) { this.name = value; } get myName() { return this.name; } }
このクラスは、名前を保持する「name」という単純なプロパティが登録されており、ゲッターでその名前を取得できるようになっていますね。
しかし、ゲッターを使わなくても直接プロパティを指定することで名前が取得できてしまっているのが分かります。
var taro = new User1('太郎'); //直接プロパティを指定 console.log(taro.name); //ゲッターを利用 console.log(taro.myName);
実行結果
太郎 太郎
このように「taro.name」へ直接アクセスできるため、どこからでも値が変更されてしまう危険性があるわけです。
Symbolを利用したプロパティの作り方
簡単な手順でプロパティに直接アクセスできないようにするため、新しく導入された「Symbol」を使ってみましょう!
Symbolを使うと、固有のユニークなIDを自動生成してくれるので完全に異なる変数を作成できるという特徴があります。
例えば、次のサンプル例を見てください!
const sample1 = Symbol('sample'); const sample2 = Symbol('sample'); console.log(sample1 === sample2);
実行結果
false
まったく同じSymbolを2つの変数へ代入しているのが分かりますね。
しかし、比較してみるとそれぞれの変数は異なるため結果的に「false」になっているのが分かります。
つまり、固有のIDが割り振られているため同じ変数にはならないわけです。
これを応用して、クラスのプロパティに導入してみましょう!
var User2 = (function() { const name = Symbol('name'); class User2 { constructor(value) { this[name] = value; } get myName() { return this[name]; } } return User2; })();
基本的なクラスの構造は同じですが、Symbolを使ってプロパティを作成しているのが分かりますね。
また、即時関数でクラスごと囲むことで外部からのプロパティへのアクセスを遮断しています。
このようなクラス構造で、もう一度プロパティへ直接アクセスしてみましょう!
var sato = new User2('佐藤'); //直接プロパティを指定 console.log(sato[name]); //ゲッターを利用 console.log(sato.myName);
実行結果
undefined 佐藤
「sato[name]」のように直接プロパティへアクセスできないことが分かりますね。
アクセスする場合はゲッターを利用するという正常な状態になるわけです。
しかしながら、「Object.getOwnPropertySymbols()」を利用するとSymbolを取得できるので完全にアクセスできないわけではない点には注意しましょう。
クラスの継承
この章では、クラスの継承について詳しく見ていきましょう!
主に、「extends」を使った継承方法と「super」を使ったオーバーライドについて学んでいきます。
「extends」を使った継承方法
class構文を利用した継承の方法について見ていきましょう。
class構文を使うと旧来の継承方法よりも簡単に記述できるようになります。
例えば、次のような「User」クラスがあるとします。
class User { constructor(name, age) { this.name = name; this.age = age; } }
これは単純に「name」「age」というプロパティをコンストラクタで定義したシンプルなクラスです。
このUserクラスを継承して新しいクラスを作成するには、次のように記述します。
class Man extends User { speak() { console.log(this.name + 'さん、こんにちは'); } }
この例では、extendsを使って「User」クラスを継承しているのがわかりますね。
注目して欲しいのは、「Man」クラスにコンストラクタを記述していない点です。
そして、次のようにインスタンスを生成して利用してみましょう!
var taro = new Man( '太郎', 34 ); taro.speak();
実行結果
太郎さん、こんんちは
メソッドはManクラスのメソッドですが、「User」クラスのコンストラクタが使われているのがわかります。
つまり、Userクラスが正常に継承されているわけです。
「super」を使ったオーバーライドの方法
基本的な継承が分かったところで、今度は継承元のプロパティやメソッドをオーバーライドする方法について見ていきましょう!
オーバーライドというのは、簡単に言うと継承元のプロパティ・メソッドを上書きするという意味になります。
例えば、次のようなクラスがあるとします。
class User { constructor(name) { this.name = name; } get myName() { return this.name; } }
これは、名前を保持するプロパティとその名前を返すメソッド(ゲッター)が登録されていますね。
このクラスを継承して新しく「Person」というクラスを作ってみましょう。
class Person extends User { constructor(userName, userAge) { this.name = userName; this.age = userAge; } }
これは名前と年齢を保持するプロパティを持っていますね。
このPersonクラスを使って次のように実行してみます!
var taro = new Person('太郎', 30); console.log(taro.myName);
しかしながら、これは結果的に「myName」が使えずにエラーになるという点に注目しましょう!
PersonクラスはUserクラスを継承しているので、普通に考えれば「myName」メソッドが利用できるはずです。
しかし、Personクラス内で新しくコンストラクタを定義しているので、継承元のコンストラクタが使われずに正しく機能していないのです。
このような状況下で、継承元のコンストラクタを使いつつ新しいプロパティも定義したい場合にオーバーライドが効果的なのです。
次のサンプル例を見てください!
class Person extends User { constructor(userName, userAge) { super(userName); this.age = userAge; } }
この例では、「super()」を使うことで継承元のプロパティにアクセスして名前を保持しているわけです。
このように記述することで「myName」メソッドは正しく動作するようになります。
まとめ
今回は、JavaScriptで新しく導入されたclass構文について学習をしました!
最後に、もう一度ポイントをおさらいしておきましょう!
・class構文を使うことでコンストラクタやメソッドを簡単に定義できる
・ゲッター・セッターメソッドは、「get / set」キーワードを利用する
・extendsを利用することで継承も簡単に利用できる
上記内容を踏まえて、ぜひみなさんも活用してみてください!