はじめに
この記事では、JavaScriptにおけるオブジェクト、プロトタイプ、クラスといった、オブジェクト指向プログラミング(OOP)の重要な要素について説明します。
JavaScriptにおけるオブジェクト
JavaScriptにおけるオブジェクトは、キーと値のコレクションを格納することができる基本的なエンティティです。キーは通常、文字列(またはシンボル)であり、値は数値、文字列、ブール値、関数、配列、他のオブジェクトなど、JavaScriptの有効なデータ型であることができます。オブジェクトは、コード内で複雑な現象を表現するための強力なデータ構造です。
オブジェクトの作成
JavaScriptにおけるオブジェクトの作成はいくつかの方法で行うことができます。もっとも一般的な方法は、オブジェクトリテラルを使用することです。オブジェクトリテラルは波括弧{}
内に定義されます。
let car = {
make: "Tesla",
model: "Model 3",
color: "red",
drive: function() {
console.log("The car is driving");
}
};
別の方法として、new Object()
コンストラクタを使用する方法があります。
let car = new Object();
car.make = "Tesla";
car.model = "Model 3";
car.color = "red";
car.drive = function() {
console.log("The car is driving");
};
オブジェクトのプロパティとメソッド
JavaScriptのオブジェクトでは、キーはプロパティとして知られ、値はそれに対応するプロパティの値として知られます。プロパティの値が関数である場合、それは通常メソッドとして参照されます。プロパティはドット記法またはブラケット記法を使用してアクセスおよび変更することができます。
// Accessing properties
console.log(car.make); // Output: "Tesla"
console.log(car["color"]); // Output: "red"
// Modifying properties
car.make = "BMW";
car["color"] = "blue";
// Calling methods
car.drive(); // Output: "The car is driving"
オブジェクトと参照
JavaScriptでは、オブジェクトは参照型です。つまり、オブジェクトを新しい変数に割り当てるときに、オブジェクトの新しいコピーを作成するのではなく、同じオブジェクトへの新しい参照を作成します。
let car1 = car;
car1.model = "Model S";
console.log(car.model); // Output: "Model S"
例えば、car1.model
を変更した場合、car.model
も変更されます。両者が同じオブジェクトを指しているからです。
組み込みオブジェクト
JavaScriptは、配列、日付、数学などのさまざまな機能を提供するArray
、Date
、Math
などの豊富な組み込みオブジェクトを提供しています。これらのオブジェクトには、保持するデータを操作したり、複雑な操作を実行したりするためのプロパティやメソッドがあります。
let now = new Date();
console.log(now.getFullYear()); // Output: The current year
let numbers = [1, 2, 3];
console.log(numbers.length); // Output: 3
console.log(Math.sqrt(16)); // Output: 4
JavaScriptにおけるプロトタイプ
JavaScriptの全てのオブジェクトは、プロトタイプと呼ばれる隠れたプロパティを持っており、そこからプロパティやメソッドを「継承」します。このプロトタイプオブジェクト自体もまた、自身のプロトタイプを持ち、プロトタイプチェーンが形成されます。プロトタイプチェーンの末尾には、プロトタイプがnullのオブジェクトが存在します。
プロトタイプベースの継承
JavaScriptでは、継承はプロトタイプを通じて実現されます。オブジェクトのプロパティやメソッドにアクセスしようとすると、JavaScriptはまずオブジェクト自体をチェックします。オブジェクト自体で見つからない場合は、プロトタイプチェーンをたどり、要求されたプロパティが見つかるか、チェーンの末尾に達するまで探します。
以下は例です。
let vehicle = {
wheels: 4,
start: function() {
console.log("The engine is starting");
}
};
let car = Object.create(vehicle);
console.log(car.wheels); // Output: 4
car.start(); // Output: "The engine is starting"
この場合、car
には独自のwheels
プロパティやstart
メソッドはありません。しかし、vehicle
というプロトタイプを通じてこれらのプロパティやメソッドにアクセスすることができます。
プロトタイプチェーン
プロトタイプチェーンとは、プロトタイププロパティを介してリンクされたオブジェクトのチェーンを指す用語です。チェーンは特定のオブジェクトから始まり、そのプロトタイプに移動し、プロトタイプを上にたどってnull
に到達するまで続きます。
以下は例です。
let vehicle = {
wheels: 4
};
let car = Object.create(vehicle);
car.make = "Tesla";
let myCar = Object.create(car);
myCar.owner = "John Doe";
console.log(myCar.wheels); // Output: 4
console.log(myCar.make); // Output: "Tesla"
console.log(myCar.owner); // Output: "John Doe"
この場合、myCar
オブジェクトはプロトタイプチェーンを通じてcar
とvehicle
からプロパティを継承しています。
__proto__ プロパティ
オブジェクトのプロトタイプは__proto__
プロパティを介してアクセスすることができます。ただし、このプロパティは本番コードで使用することは推奨されていません。なぜなら、全てのJavaScript環境でサポートされていない上に、Object.getPrototypeOf()
に置き換えられており、非推奨とされているからです。
console.log(car.__proto__ === vehicle); // Output: true
console.log(Object.getPrototypeOf(car) === vehicle); // Output: true
組み込みプロトタイプ
Array、Object、Functionなどの組み込みJavaScriptコンストラクタには、それらのインスタンスに関連するメソッドやプロパティが含まれるプロトタイプがあります。例えば、Array.prototype
にはpush()
、pop()
、map()
、filter()
などのメソッドが含まれています。
let arr = [1, 2, 3];
console.log(arr.__proto__ === Array.prototype); // Output: true
JavaScriptにおけるクラス
JavaScriptでは、クラスはES6で導入された関数の一種であり、コンストラクタ関数とプロトタイプメソッドを定義するための伝統的な見た目の方法を提供します。クラスはオブジェクトの作成や継承に対してクリーンでエレガントな構文を提供します。
クラスの作成
クラスを定義するには、class
キーワードの後にクラスの名前を続けます。クラス内部では、そのクラスで作成されたオブジェクトを作成して初期化するための特別なメソッドであるconstructor
関数を定義することができます。
class Car {
constructor(make, model) {
this.make = make;
this.model = model;
}
startEngine() {
console.log(`${this.make} ${this.model}'s engine is starting`);
}
}
let myCar = new Car("Tesla", "Model 3");
myCar.startEngine(); // Output: "Tesla Model 3's engine is starting"
クラスの継承
クラスでは、extends
キーワードを使用して継承を実装することができます。extends
で作成されたクラスは、親クラスから全てのメソッドを継承します。
class ElectricCar extends Car {
constructor(make, model, batteryCapacity) {
super(make, model);
this.batteryCapacity = batteryCapacity;
}
chargeBattery() {
console.log(`${this.make} ${this.model}'s battery is charging`);
}
}
let myElectricCar = new ElectricCar("Tesla", "Model S", 100);
myElectricCar.startEngine(); // Output: "Tesla Model S's engine is starting"
myElectricCar.chargeBattery(); // Output: "Tesla Model S's battery is charging"
プライベートおよびパブリッククラスフィールド
デフォルトでは、JavaScriptクラスの全てのプロパティとメソッドはパブリックです。ただし、ES2020では、ハッシュ#
を接頭辞として使用してプライベートクラスフィールドを作成する方法が導入されました。
class Car {
#speed = 0;
accelerate() {
this.#speed += 10;
console.log(`Speed: ${this.#speed}`);
}
}
let myCar = new Car();
myCar.accelerate(); // Output: "Speed: 10"
console.log(myCar.#speed); // Throws an error: Property '#speed' is private and only accessible within class 'Car'.
スタティックメソッドとプロパティ
スタティックメソッドとプロパティは、クラス自体に属するものであり、クラスのインスタンスではないものです。スタティックメソッドやプロパティを宣言するには、static
キーワードを使用します。
class Car {
static numberOfWheels = 4;
static displayNumberOfWheels() {
console.log(`A car typically has ${this.numberOfWheels} wheels`);
}
}
Car.displayNumberOfWheels(); // Output: "A car typically has 4 wheels"