はじめに
JavaScriptのクラスについて勉強をしていたので、学習内容を備忘録として残す。
クラスとは
クラスとは、動作や状態を定義した構造のこと。 抽象的にわかりにくかもしれないが、よく例にされるのは、車の設計図(クラス:Car によって作られるのが、インスタンスオブジェクト:車種) の話。
https://dd-career.com/blog/it-takatsuki_20220209/
JavaScriptでは、ES2015より以前はclass構文はなく、関数を使って疑似的にクラスのようなものを表現していた。 ちなみに、このclass構文で定義されたクラスは関数オブジェクトの一種。
クラス構文
class MyClass {
constructor() {
// コンストラクタ関数の処理
// インスタンス化されるときに自動的に呼び出される
}
}
class MyClassB {
// コンストラクタの処理が不要な場合は省略できる
}
インスタンス化
クラスからインスタンスを作成することをインスタンス化と呼ぶ。 あるインスタンスが指定したクラスから作成されたものかを真偽値で判定するinstanceof演算子というものもある。
//Carクラスの定義
class Car{}
// `Car`をインスタンス化する
const bus = new Car();
// 毎回新しいインスタンス(オブジェクト)を作成する
const truck = new Car();
// それぞれのインスタンスは異なるオブジェクト
console.log(bus === truck); // => false
// クラスのインスタンスかどうかは`instanceof`演算子で判定できる
console.log(bus instanceof Car); // => true
console.log(truck instanceof Car); // => true
コンストラクタの処理
class Hoge{
// 2. コンストラクタ関数の仮引数として`x`には`3`、`y`には`4`が渡る
constructor(x, y) {
// 3. インスタンス(`this`)の`x`と`y`プロパティにそれぞれ値を設定する
this.x = x;
this.y = y;
// コンストラクタではreturn文は書かない
}
}
// 1. コンストラクタを`new`演算子で引数とともに呼び出す
const hoge = new Hoge(3, 4);
// 4. `Hoge`のインスタンスである`hoge`の`x`と`y`プロパティには初期化された値が入る
console.log(hoge.x); // => 3
console.log(hoge.y); // => 4
メモ
JavaScriptでは慣習としてクラス名には大文字ではじまる名前を付ける。
プロトタイプメソッド
インスタンス間で共有されるメソッドのことをプロトタイプメソッドと呼ぶ。
class Counter {
constructor() {
this.count = 0;
}
// `increment`メソッドをクラスに定義する
increment() {
// `this`は`Counter`のインスタンスを参照する
this.count++;
}
}
const counterA = new Counter();
const counterB = new Counter();
// `counterA.increment()`のベースオブジェクトは`counterA`インスタンス
counterA.increment();
// 各インスタンスの持つプロパティ(状態)は異なる
console.log(counterA.count); // => 1
console.log(counterB.count); // => 0
// 各インスタンスオブジェクトのメソッドは共有されている(同じ関数を参照している)
console.log(counterA.increment === counterB.increment); // => true
Publicクラスフィールド
引数を渡さない場合でも、初期化をするためにconstructorメソッドを書かないといけない。めんどくさすぎる・・・。 そこで誕生したのが、クラスフィールド構文。 クラスフィールドでは、クラスのインスタンスが持つプロパティを以下のように定義することができる。
class クラス {
プロパティ名 = プロパティの初期値;
}
class Counter {
count = 0;
increment() {
this.count++;
}
}
const counter = new Counter();
counter.increment();
console.log(counter.count); // => 1
アクセッサプロパティの定義
クラスでは、プロパティの参照(getter)、プロパティへの代入(setter)時に呼び出される特殊なメソッドを定義できる。 このメソッドはプロパティのように振る舞うためアクセッサプロパティと呼ばれる。
class クラス {
// getter
get プロパティ名() {
return 値;
}
// setter
set プロパティ名(仮引数) {
// setterの処理
}
}
const インスタンス = new クラス();
インスタンス.プロパティ名; // getterが呼び出される
インスタンス.プロパティ名 = 値; // setterが呼び出される
constructorとの併用も可能
class MyClass {
publicField = 1;
constructor(arg) {
this.property = arg;
}
}
const myClass = new MyClass(2);
console.log(myClass.publicField); // => 1
console.log(myClass.property); // => 2
publicFieldのようにクラスの外からアクセスできるプロパティを定義するクラスフィールドをPublicクラスフィールドと呼ぶ。
thisについて
クラスフィールでのthisは、そのクラスのインスタンスを参照します。 アロー関数と組み合わせると最強。
class Counter {
count = 0;
// クラスフィールドでの`this`はクラスのインスタンスとなる
// upメソッドは、クラスのインスタンスに定義される
up = () => {
this.increment();
};
increment() {
this.count++;
}
}
const counter = new Counter();
// Arrow Functionなので、thisはクラスのインスタンスに固定されている
const up = counter.up;
up();
console.log(counter.count); // => 1
// 通常のメソッド定義では、`this`が`undefined`となってしまうため例外が発生する
const increment = counter.increment;
increment(); // Error: Uncaught TypeError: this is undefined
Privateクラスフィールド
Publicクラスフィールドとは反対で、クラス外からアクセスされたくないインスタンスプロパティに対して設定する。 定義したPrivateクラスフィールドは、this.#フィールド名で参照できます。
class クラス {
// プライベートなプロパティは#をつける
#フィールド名 = プロパティの初期値;
}
lass NumberWrapper {
// valueはPrivateクラスフィールドとして定義
#value;
constructor(value) {
this.#value = value;
}
// `#value`フィールドの値を返すgetter
get value() {
return this.#value;
}
// `#value`フィールドに値を代入するsetter
set value(newValue) {
this.#value = newValue;
}
}
const numberWrapper = new NumberWrapper(1);
// クラスの外からPrivateクラスフィールドには直接はアクセスできない
console.log(numberWrapper.#value); // => SyntaxError: reference to undeclared private field or method #value
静的メソッド
通常、クラスはインスタンス化(new)して使うが、インスタンス化せずに利用できる静的メソッド(クラスメソッド)がある。 「static」を付ける。
class ArrayWrapper {
// new演算子で引数が渡されたなかった場合の初期値は空配列
constructor(array = []) {
this.array = array;
}
// rest parametersとして要素を受けつける
static of(...items) {
return new ArrayWrapper(items);
}
get length() {
return this.array.length;
}
}
// 配列を引数として渡している
const arrayWrapperA = new ArrayWrapper([1, 2, 3]);
// 要素を引数として渡している
const arrayWrapperB = ArrayWrapper.of(1, 2, 3);
console.log(arrayWrapperA.length); // => 3
console.log(arrayWrapperB.length); // => 3
今回はとりあえずここまで。