JavaScript クラス定義の基礎
今回もJavaScriptの基礎的な部分で曖昧な部分を備忘録として残しておきます
今回はクラス定義についてということで、クラス定義から、静的メソッドやゲッターセッターについても書き記せたらと思います
クラスの定義
クラス名はパスカルケース(単語の先頭を大文字)を使いますが、プロパティ名やメソッド名はキャメルケース(単語の先頭を小文字)を使います
そもそもクラスとはオブジェクトを作成するための雛形のようなものです
// ユーザーを作成するクラスの定義 class User { constructor(username, password) { this.username = username; this.password = password; } login() { console.log(`ログイン [ ${this.username} / ${this.password} ]`); } } // オブジェクトの作成 : 太郎 const taro = new User("太郎", "taro-pwd"); // オブジェクトの作成:花子 const hanako = new User("花子", "hanako-pwd");
クラスはあくまで設計図のようなもので書いただけ、定義しただけでは、実態を持ちません
どの言語でもそうですが使用するためにはインスタンス化を行います
インスタンス化
const オブジェクト = new クラス名( [引数1, 引数2, ...] );
ちなみに引数がない場合は()は省略できるようです
コンストラクター
constructor()
はインスタンス化された際に最初に実行される特別な関数で引数などを受け取っていた場合ここで変数などに紐付けて初期化処理を行います
コンストラクタの実行を終えると、その中で使われているthisがnew演算子の結果として返されます。
そのため、生成されるインスタンスにプロパティを設定したい場合には「this.プロパティ」のようにthisに対してプロパティを追加します
クラス内で記述するthisキーワードは生成されるインスタンスを表すことを覚えておきましょう
したがってクラス内で自身のインスタンスのプロパティやメソッドを参照する場合は「this」を付けます
静的メソッドと静的プロパティ
静的メソッドと静的プロパティの解説です
これはクラスから直接プロパティやメソッドを使用できるものです
つまりインスタンス化が不要でそのまま使用することが出来ます。しかし参照元に対しての仕様などわかりにくい点などもあるため、ここでしっかり解説しておきます
以下は定義方法と使用例です
class Human { static TYPE = "普通の人"; // 静的プロパティを定義 static staticMove() { // 静的メソッドを定義 console.log(Human.TYPE + "は歩いて移動します。"); } constructor(name) { this.name = name; } move() { // インスタンスメソッド console.log(this.name + "は歩いて移動します。"); } } const taro = new Human("太郎"); Human.staticMove(); // > 普通の人は歩いて移動します。 console.log(Human.TYPE); // > 普通の人 taro.move(); // > 太郎は歩いて移動します。
静的プロパティや静的メソッドを定義するにはstatic
キーワードを付与します
静的プロパティや静的メソッドはスタティックプロパティやスタティックメソッドと呼ぶこともあるようです
そしてそれらにアクセスするためにはクラス名に続けてプロパティ名メソッド名を記述します。
インスタンス化したオブジェクトに続けて静的プロパティや静的メソッドを記述することは出来ません。
そのためスタティックの中ではthis
を使ってもインスタンスを取得できません
thisキーワードが使えるのは「オブジェクト.メソッド名()」の形式でメソッドが実行されて、インスタンオブジェクトが特定される場合のみです
インスタンスから静的メソッドと静的プロパティへアクセスする
インスタンスメソッド内から静的メソッドや静的プロパティにアクセスする場合は、this.constructor.静的メソッド
のように記述することが可能です
class TestClass { static STATIC_PROP = "静的プロパティ"; static staticMethod() { return "静的メソッド" }; method() { console.log(this.constructor.STATIC_PROP); console.log(this.constructor.staticMethod()); } } const test = new TestClass; test.method(); > 静的プロパティ > 静的メソッド
ここまでのまとめとして参照できるものを確認しておきます
- 静的メソッドから他の静的メソッドや静的プロパティはクラス名を使って参照できる
- 静的メソッドからインスタンスのメソッドやプロパティは参照できません
- インスタンスメソッドから他の静的メソッドや静的プロパティはthis.constructorを使って参照できる
- インスタンスメソッドから他のインスタンスのメソッドやプロパティはthisを通して参照できます
ゲッターとセッター
使用どころが不明ですが、、、残しておきます、、
クラスを記述しているとプロパティの値を取得・変更する時に特定の処理も合わせて実行したい場合があります。
その時に使うのがゲッター(ゲッターメソッド)、セッター(セッターメソッド)です
これを定義するのはそれぞれメソッド名の前にgetキーワード
、setキーワード
を付与します
class クラス名 {
get ゲットプロパティ() {
return ゲットプロパティを参照した際に取得される値;
}
set セットプロパティ() {
プロパティに値を設定する時に実行したい処理;
}
}
こうした定義したメソッドの呼び出し方 (実行方法) です
const obj = new クラス名;
console.log(obj.ゲットプロパティ);
obj.セットプロパティ = 値;
このように通常のメソッドと違い、ゲッターセッターを実行するときには、末尾の()は必要ありません
オブジェクトのように値の変更・取得を行えば実行されます
そのため他のプロパティ名と重複しないように気をつけなければなりません
次は簡単ですが使用例を見て解説してみます
class Person { constructor(firstname, lastname) { this.firstname = firstname; this.lastname = lastname; /* this.fullnameの定義は書かない! */ } get fullname() { // fullnameが取得された場合には氏名を結合して返す return this.lastname + this.firstname; } set age(value) { // ageに値が設定された場合には _ageに数値を設定 this._age = Number(value); } get age() { // ageが取得された場合には値が保持されている _ageの値を返す return this._age; } } const taro = new Person("太郎", "独習"); // ゲッターを通して値を取得 console.log(taro.fullname); // ① // > 独習太郎 // セッターを通して文字列で値を設定 taro.age = "18"; // ② // ゲッターを通して値を取得 console.log(typeof taro.age); // taro._ageの値が返される // > number 数値型が取得される // オブジェクトの状態を確認 console.log(taro); // > { firstname: "太郎", lastname: "独習", _age: 18 } //③
①上記の例ではfullnameというゲッターを定義し、taro.fullnameとして実行しその結果が返り値になっています
②ageセッターでは代入演算子で設定された値 (“18”) がセッターの引数 (value) に渡されますageセッターではNumberによって文字列が数値に変換されて、_ageプロパティに設定されます。
そのため、taro.ageゲッターで取得した値は、数値型になります
③_ageプロパティにageの値は保持されています。オブジェクトのプロパティに_ageプロパティが追加されていてその中に実際の値が格納されていることになります
クラスの継承
クラスの継承とは既存のクラスを継承する(引き継ぐ)ことで、既存のクラスの機能を利用して、少し機能の異なるクラスを新たに生成する記法です
class Parent { constructor(value) { this.ParentProp = value; } parentMethod() { console.log("親クラスのメソッド"); } } class Child extends Parent { constructor(ParentProp, childProp) { super(ParentProp); this.childProp = childProp; } childMethod() { console.log(`子から親にアクセス[${this.ParentProp}]`); } } const childObject = new Child("親", "子"); console.log(childObject); childObject.parentMethod(); childObject.childMethod();
extendsにてParentクラスを継承しています
なお複数のクラスを継承することはできません
super()メソッド
子クラスコンストラクタを記述する場合は、必ずsuperキーワードで親クラスのコンストラクタを実行します
このメソッドを呼び出さないとエラーになるため注意が必要です。
またsuperメソッド実行前にthisキーワードにアクセスした場合もエラーになります
そのメソッド実行後thisキーワードを通して親クラスのメソッドにアクセスすることが可能になります
プライベートのアクセス権を追加する
他のプログラミング言語の例にもれずJavaScriptでも自クラス内からのみアクセス可能なプライベートなプロパティやメソッドを定義することができるようになったようです
パブリックなプロパティやメソッド
パブリックはクラスの外からでもアクセス可能な状態を指します
基本的に何も指定しないで定義した場合これに該当します
プライベートなプロパティやメソッド
プライベートは自クラス内からのみアクセス可能な状態を指します。
プライベートプロパティやプライベートメソッドを定義するには先頭に「 # 」を付けます
class Counter { #count = 0; // プライベートプロパティ #print() { // プライベートメソッド console.log(this.#count); } increment() { // パブリックメソッド this.#count++; this.#print(); // プライベートメソッドを実行 } } const counter = new Counter(); counter.increment(); // パブリックメソッドを実行 // > 1 counter.#count = 10; // プライベートプロパティはクラス外からアクセス不可! counter.#print(); // プライベートメソッドはクラス外からの実行不可!
このようにプライベートのアクセス権を使うことによって、クラス外から参照できるプロパティやメソッドを明確に記述できるようになるため、より厳格にクラスを定義できます
※プライベートプロパティを使用する際はクラスのトップレベルで宣言する必要があるため、注意しましょう
今回はここまでにします
次回以降でクラスに関係の深い、プロトタイプについてしっかりと掘り下げてまとめて見る予定です
頑張りましょう
コメントをお待ちしております