クラスとは
ES6で導入されたクラスについて。
親クラス(スーパークラス)、子クラス、コンストラクタ、インスタンス・・などがあり、
クラスを使うと、コードのメンテナンスがしやすくなり、再利用性が上がるというメリットがあります。
// 親クラスの定義
class Animal {
constructor(name) {
this.name = name;
}
// 親クラスのメソッド
sound() {
console.log("Animal makes a sound");
}
}
// 子クラスの定義
class Dog extends Animal {
constructor(name, breed) {
super(name); // 親クラスのコンストラクタを呼び出す
this.breed = breed;
}
// 子クラスのメソッド
sound() {
console.log("Woof!");//親クラスのメソッドをオーバーライド
}
// 子クラスの独自のメソッド
wagTail() {
console.log("Dog wags its tail");
}
}
// 親クラスのインスタンス作成
const animal = new Animal("Generic Animal");
console.log(animal.name); // "Generic Animal"
animal.sound(); // "Animal makes a sound"
// 子クラスのインスタンス作成
const dog = new Dog("Buddy", "Golden Retriever");
console.log(dog.name); // "Buddy"
console.log(dog.breed); // "Golden Retriever"
dog.sound(); // "Woof!"
dog.wagTail(); // "Dog wags its tail"
ゲッター、セッター
そしてゲッター、セッターについて。
class Circle {
constructor(radius) {
this._radius = radius;
}
// ゲッター
get radius() {
return this._radius;
}
// セッター
set radius(newRadius) {
if (newRadius > 0) {
this._radius = newRadius;
} else {
console.error("Radius must be a positive number.");
}
}
}
const circle = new Circle(5);
// ゲッターを使用してプロパティにアクセスし、プロパティの値を取得します
console.log(circle.radius); // 出力: 5
// セッターを使用してプロパティに新しい値を設定します
circle.radius = 10; // 正常に動作します
// ゲッターを使用してプロパティの値を取得します
console.log(circle.radius); // 出力: 10
// セッターを使用してプロパティに負の値を設定しようとしますが、セッターが定義された条件に反しているためエラーが発生します
circle.radius = -5; // エラー: Radius must be a positive number.
// プロパティは変更されず、以前の値が維持されています
console.log(circle.radius); // 出力: 10
ゲッターだけを設定すると、プロパティの値を変更不可にできます。
class Circle {
constructor(radius) {
this._radius = radius;
}
get radius() {
return this._radius;
}
// このようにセッターを提供しないことで、半径を読み取り専用にする
}
const circle = new Circle(5);
console.log(circle.radius); // プロパティの値を取得する
circle.radius = 10; // エラー: ゲッターのみが定義されているため、プロパティの値を変更できません
一方セッターは、条件を満たさない値がセットされないようにすることができます。
class Rectangle {
constructor(width, height) {
this._width = width;
this._height = height;
}
// セッター
set width(newWidth) {
if (newWidth > 0) {
this._width = newWidth;
} else {
console.error("Width must be a positive number.");
}
}
}
const rectangle = new Rectangle(5, 10);
rectangle.width = -5; // Width must be a positive number.
クロージャー
クロージャーとは、関数がスコープ外で呼び出された時に、元のスコープ内の変数を覚えている機能です。
function Animal(name) {
let _name = name; // プライベート変数
this.getName = function() {
return _name;
};
this.setName = function(newName) {
_name = newName;
};
}
const dog = new Animal('Rover');
console.log(dog.getName()); // 出力: Rover
dog.setName('Spot');
console.log(dog.getName()); // 出力: Spot
console.log(dog._name); // 出力: undefined (外部からはアクセスできない)
クラスを使う場合は以下のようになります。
class Animal {
constructor(name) {
let _name = name; // プライベート変数
this.getName = function() {
return _name;
};
this.setName = function(newName) {
_name = newName;
};
}
}
const cat = new Animal('Whiskers');
console.log(cat.getName()); // 出力: Whiskers
cat.setName('Tom');
console.log(cat.getName()); // 出力: Tom
console.log(cat._name); // 出力: undefined (外部からはアクセスできない)
最初のクラスの例と何が違うかというと、
// 親クラスの定義
class Animal {
constructor(name) {
this.name = name;
}
この this.name = name; が let _name = name; になっている箇所が違います。
クラス構文ではプライベートフィールドとして # を使う方法が標準化されています。
class Animal {
#name; // プライベートフィールド
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
setName(newName) {
this.#name = newName;
}
}
const bird = new Animal('Tweety');
console.log(bird.getName()); // 出力: Tweety
bird.setName('Polly');
console.log(bird.getName()); // 出力: Polly
console.log(bird.#name); // エラー: プライベートフィールドに直接アクセスできない
プライベートフィールドにすることで、カプセル化ができます。
カプセル化とは、
データとそれに関連するメソッドを一つの単位にまとめ、その内部構造を外部から隠すこと。
よって、上の例ではbird.#nameは直接アクセスできないようになります。
カプセル化はES6のクラス構文や即時関数(IIFE)で使うことができます。