JavaScriptにおけるオブジェクト指向プログラミング
JavaScriptのオブジェクト指向プログラミングは、プロトタイプベースの継承を利用しています。これは、オブジェクトが他のオブジェクト(プロトタイプ)から機能を継承する仕組みです。
目次:
プロトタイプ
プロトタイプとは?
オブジェクトのプロパティやメソッドを共有するためのテンプレートオブジェクトです。
各オブジェクトは一つのプロトタイプを持ち、そのプロトタイプからメソッドやプロパティを継承します。
使い方の例
配列オブジェクトの push や pop メソッドは、各配列が持っているわけではなく、プロトタイプオブジェクトから継承されています。
Array.prototype.push などがその例です。
ファクトリ関数
定義と例
オブジェクトを作成する関数。
例: makeColor 関数
function makeColor(r, g, b) {
const color = {};
color.r = r;
color.g = g;
color.b = b;
color.rgb = function() {
return `rgb(${this.r},${this.g},${this.b})`;
};
return color;
}
const firstColor = makeColor(35, 255, 150);
欠点
各オブジェクトに同じメソッドを個別に作成してしまうため、メモリの無駄が生じる。
コンストラクタ関数
定義と例
new キーワードと共に使用される関数。新しいオブジェクトを作成し、そのプロパティとメソッドを初期化します。
例: Color コンストラクタ関数
function Color(r, g, b) {
this.r = r;
this.g = g;
this.b = b;
}
const newColor = new Color(255, 0, 0);
メリット
プロトタイプを利用することで、共通のメソッドを一箇所にまとめることができる。
プロトタイプメソッドの定義
Color.prototype.rgb = function() {
return `rgb(${this.r},${this.g},${this.b})`;
};
MDN参照:
- 空のプレーンな JavaScript オブジェクトを生成します。
- 新しいオブジェクトにプロパティ (proto) を追加し、コンストラクター関数のプロトタイプオブジェクトに結びつけます。
メモ: コンストラクター関数のプロトタイプに追加されたプロトタイプやオブジェクトは、そのためそのコンストラクター関数で(new を使用して)生成されたすべてのインスタンスからアクセスできます。
3.新しく生成されたオブジェクトインスタンスを this コンテキストとして結びつけます。 (すなわち、コンストラクター関数内の this へのすべての参照は、最初のステップで作成されたオブジェクトを参照するようになります。)
4.関数がオブジェクトを返さない場合は this を返します。
クラス構文
定義と例
ES6から導入された糖衣構文(シンタックスシュガー)で、コンストラクタ関数とプロトタイプの処理をよりシンプルに書けるようにしたもの。
例: Color クラス
class Color {
constructor(r, g, b, name) {
this.r = r;
this.g = g;
this.b = b;
this.name = name;
this.calcHSL();
}
innerRGB() {
return `${this.r}, ${this.g}, ${this.b}`;
}
rgb() {
return `rgb(${this.innerRGB()})`;
}
rgba(a = 1.0) {
return `rgba(${this.innerRGB()},${a})`;
}
hex() {
return '#' + ((1 << 24) + (this.r << 16) + (this.g << 8) + this.b).toString(16).slice(1);
}
fullySaturated() {
return `hsl(${this.h}, 100%, ${this.l}%)`;
}
hsl() {
return `hsl(${this.h}, ${this.s}%, ${this.l}%)`;
}
opposite() {
const newHue = (this.h + 180) % 360;
return `hsl(${newHue}, ${this.s}%, ${this.l}%)`;
}
calcHSL() {
let { r, g, b } = this;
r /= 255;
g /= 255;
b /= 255;
let cmin = Math.min(r, g, b),
cmax = Math.max(r, g, b),
delta = cmax - cmin,
h = 0,
s = 0,
l = 0;
if (delta == 0) h = 0;
else if (cmax == r)
h = ((g - b) / delta) % 6;
else if (cmax == g)
h = (b - r) / delta + 2;
else
h = (r - g) / delta + 4;
h = Math.round(h * 60);
if (h < 0) h += 360;
l = (cmax + cmin) / 2;
s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
this.h = h;
this.s = s;
this.l = l;
}
}
const red = new Color(255, 67, 89, 'tomato');
const white = new Color(255, 255, 255, 'white');
メリット
コンストラクタ関数とプロトタイプメソッドを一箇所にまとめて定義でき、コードが読みやすくなる。
まとめ
1.プロトタイプ
オブジェクトが共有するプロパティやメソッドのテンプレート。
2.ファクトリ関数
簡単にオブジェクトを作成できるが、メソッドの重複が生じる。
3.コンストラクタ関数
new キーワードを使って新しいオブジェクトを作成。
プロトタイプを利用してメソッドの重複を避ける。
4.クラス構文
コンストラクタ関数とプロトタイプの糖衣構文。
コードがシンプルで読みやすくなる。
上記の Color クラスでは、RGB値からHSL値を計算する calcHSL メソッドや、RGB表記、RGBA表記、HEX表記を返すメソッド、HSL表記を返すメソッド、補色を計算するメソッドなどが定義されています。これにより、色の操作が簡単に行えるようになっています。
red と white のインスタンスは、それぞれ Color クラスを使って作成され、色のプロパティやメソッドを使用することができます。
const red = new Color(255, 67, 89, 'tomato');
const white = new Color(255, 255, 255, 'white');
console.log(red.rgb()); // rgb(255, 67, 89)
console.log(white.hex()); // #ffffff
console.log(red.opposite()); // hsl(180, 100%, 50%)
JavaScriptにおける継承: extendsとsuper
JavaScriptのクラス継承は、共通の機能を一つの親クラスに集約し、子クラスでその機能を再利用することで、コードの重複を減らし、効率的に開発する手法です。
この継承はextendsキーワードとsuperキーワードを使って実現されます。
class Cat {
constructor(name, age){
this.name = name;
this.age = age;
}
eat() {
return `${this.name}がご飯を食べている。`;
}
meow(){
return 'にゃー';
}
}
class Dog {
constructor(name, age){
this.name = name;
this.age = age;
}
eat() {
return `${this.name}がご飯を食べている。`;
}
bark() {
return 'わんわん!!';
}
}
このコードでは、nameやageの初期化、eatメソッドが重複しています。
継承を使った改善
共通のプロパティとメソッドを親クラスとしてまとめ、子クラスでその親クラスを継承します。
class Pet {
constructor(name, age){
this.name = name;
this.age = age;
}
eat() {
return `${this.name}がご飯を食べている。`;
}
}
class Cat extends Pet {
meow(){
return 'にゃー';
}
}
class Dog extends Pet {
bark() {
return 'わんわん!!';
}
}
このコードでは、CatとDogが共通のプロパティやメソッドをPetクラスから継承しています。
extendsキーワード
extendsキーワードを使って、CatとDogがPetクラスを継承しています。これにより、Petクラスのプロパティやメソッドを自動的に利用できます。
class Cat extends Pet {
meow(){
return 'にゃー';
}
}
class Dog extends Pet {
bark() {
return 'わんわん!!';
}
}
superキーワード
superキーワードを使うことで、親クラスのコンストラクタを呼び出すことができます。これにより、子クラスのコンストラクタで親クラスのプロパティを初期化できます。
class Cat extends Pet {
constructor(name, age, color){
super(name, age);
this.color = color;
}
meow(){
return 'にゃー';
}
}
super(name, age)を使って、Petクラスのコンストラクタを呼び出し、nameとageを初期化しています。その後、Catクラス独自のプロパティであるcolorを追加しています。
完全な例
以下は、Petクラスを継承したCatとDogの完全な例です。
class Pet {
constructor(name, age){
this.name = name;
this.age = age;
}
eat() {
return `${this.name}がご飯を食べている。`;
}
}
class Cat extends Pet {
constructor(name, age, color){
super(name, age);
this.color = color;
}
meow(){
return 'にゃー';
}
}
class Dog extends Pet {
bark() {
return 'わんわん!!';
}
}
const myCat = new Cat('ミケ', 3, '三毛');
console.log(myCat.eat()); // ミケがご飯を食べている。
console.log(myCat.meow()); // にゃー
console.log(myCat.color); // 三毛
const myDog = new Dog('ポチ', 5);
console.log(myDog.eat()); // ポチがご飯を食べている。
console.log(myDog.bark()); // わんわん!!
extends: クラスを継承するためのキーワード。
子クラスが親クラスのプロパティやメソッドを利用できるようになります。
super: 親クラスのコンストラクタやメソッドを呼び出すためのキーワード。
子クラスのコンストラクタで親クラスのプロパティを初期化する際に使います。
継承を使うことで、コードの重複を避け、再利用性を高めることができます。親クラスに共通の機能を持たせ、子クラスで独自の機能を追加することで、効率的な開発が可能となります。