classとは
JavaScriptのclass
は、オブジェクト指向プログラミング(OOP)の概念を実現するための構文です。クラスは、同じプロパティやメソッドを持つオブジェクトの設計図となるものです。
class
キーワードを使用してクラスを宣言し、そのクラス内でプロパティやメソッドを定義します。クラス内で定義されたメソッドは、そのクラスから作成されたオブジェクト(インスタンス)で使用することができます。
以下に、JavaScriptのclass
の基本的な構文を示します。
class MyClass {
constructor() {
// コンストラクタ: インスタンスを作成する際に実行されるメソッド
// インスタンス固有の初期化処理を行う
}
method1() {
// メソッド1の定義
}
method2() {
// メソッド2の定義
}
}
上記の例では、MyClass
というクラスを定義しています。constructor
メソッドは、new
キーワードを使用してインスタンスを作成する際に呼び出される特別なメソッドです。その他のメソッドは、クラスから作成されたインスタンスで呼び出すことができます。
クラスからインスタンスを作成するには、new
キーワードを使用します。
const myObject = new MyClass();
myObject
はMyClass
のインスタンスであり、MyClass
のメソッドを呼び出すことができます。
クラスは、コードの再利用性やメンテナンス性を向上させるための重要な機能です。また、クラスの継承やポリモーフィズムなどのOOPの概念を利用することもできます。
巻き上げ (ホイスティング)
JavaScriptのclass
定義は、通常の関数宣言とは異なり、巻き上げ(ホイスティング)の動作が異なります。
巻き上げ(ホイスティング)とは、コードの実行前に関数や変数の宣言がスコープの先頭に移動する動作のことを指します。通常の関数宣言では、関数は巻き上げられて関数の宣言が行われる前に呼び出すことができます。
しかし、class
の場合は、クラスの宣言自体は巻き上げられますが、クラスのメソッドは巻き上げられません。つまり、クラスを宣言する前にクラスのコンストラクタやメソッドを呼び出すことはできません。
以下に、class
の巻き上げの例を示します。
const myObject = new MyClass();
class MyClass {
constructor() {
this.name = "John";
}
sayHello() {
console.log(`Hello, ${this.name}!`);
}
}
上記の例では、MyClass
のインスタンスを作成する前にnew MyClass()
という行があります。通常の関数ならば、関数の宣言が巻き上げられるため、これは問題ありません。しかし、class
の場合は、MyClass
が宣言される前にMyClass
のインスタンスを作成しようとすると、エラーが発生します。
class
の巻き上げの挙動に注意する必要があります。クラスを使用する場合は、クラスの宣言よりも後ろでクラスのインスタンスを作成するようにしてください。
クラス式
クラス式は、名前を持つ場合と持たない場合の2つの形式で定義できます。
名前を持たないクラス式の場合、クラス自体には名前がありません。例えば、以下のように定義されるクラスは無名クラス式です。
let Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// 出力: "Rectangle"
無名クラス式の場合、Rectangle
という変数に代入されたクラスオブジェクトを参照することができます。また、クラスオブジェクトのname
プロパティを通じて、クラスの名前にアクセスすることもできます。
一方、名前を持つクラス式の場合、クラス自体に名前があります。以下のように定義されるクラスは名前付きクラス式です。
let Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
console.log(Rectangle.name);
// 出力: "Rectangle2"
名前付きクラス式では、クラス内部でその名前を使用することができます。また、クラスオブジェクトのname
プロパティを通じて、クラスの名前にアクセスすることもできます。
名前付きクラス式は、クラス内で自己参照する場合やデバッグ情報などで使用されることがあります。一方、無名クラス式は、一度だけ使用する小さなクラスを定義する際などに便利です。
classで使用するconstructor()
constructor()
は、JavaScriptのclass
内で使用される特別なメソッドです。クラスから新しいインスタンス(オブジェクト)を作成する際に、new
キーワードと共に自動的に呼び出されます。constructor()
メソッドは、インスタンスの初期化処理を行うために使用されます。
以下に、constructor()
メソッドの詳細と使い方を説明します。
-
constructor()
メソッドの定義:
class MyClass {
constructor(parameter1, parameter2) {
// 初期化処理を記述
}
}
- メソッドを定義します。
constructor()
メソッドは、クラス内で1つだけ存在できます。 - パラメータの受け取り:
constructor()
メソッドは、インスタンス作成時に渡される引数を受け取ることができます。引数は、クラスのインスタンスごとに異なる値を指定するために使用されます。引数はクラスの初期化に必要なデータを提供します。 - インスタンス変数の初期化:
constructor()
メソッド内で、インスタンス変数(クラス内のプロパティ)を初期化することができます。インスタンス変数には、クラス内の他のメソッドからアクセスできます。通常、this
キーワードを使用してインスタンス変数を参照します。 - 初期化処理の実行:
constructor()
メソッド内に、インスタンスの初期化に必要な処理を記述します。これには、インスタンス変数の初期化、他のオブジェクトとの関連付け、外部リソースの取得などが含まれます。
以下は、constructor()
メソッドを使用した例です。
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
console.log("Personインスタンスが作成されました");
}
}
const person1 = new Person("Alice", 25);
console.log(person1.name); // 出力: "Alice"
console.log(person1.age); // 出力: 25
上記の例では、Person
クラスのconstructor()
メソッドが定義されています。constructor()
メソッドは2つの引数を受け取り、name
とage
というインスタンス変数を初期化します。console.log()
文は、constructor()
メソッドが呼び出されたことを示すために表示されます。
classを定義する際に必ずconstructorが必要か
class
を定義する際には必ずしもconstructor
を記述する必要はありません。constructor
は、クラスがインスタンス化されるときにインスタンスの初期化を行うために使用される特別なメソッドです。
constructor
が定義されていない場合、class
はデフォルトの空のconstructor
を持つことになります。この場合、インスタンス化されたオブジェクトは特別な初期化処理を持たず、class
のプロパティやメソッドのみを利用できます。
以下はconstructor
を省略した場合の例です:
class MyClass {
// constructorが省略されている
method() {
// クラスのメソッド
}
}
この場合、MyClass
のインスタンスを作成するときには引数を指定せずに作成することができます。ただし、インスタンスが作成される際には自動的に空の初期化処理が実行されます。
constructor
が不要な場合は、class
内のプロパティやメソッドのみを使用する場合に適しています。一方、インスタンスの初期化が必要な場合や特定の設定を行いたい場合には、constructor
を定義して初期化処理を記述する必要があります。
したがって、constructor
はclass
定義内で必須ではなく、必要に応じて使用することができます。
constractorを使用しない方法
「constructor」を使用せずに、コードを書き直す例を以下に示します。
class Person {
name;
age;
setName(name) {
this.name = name;
}
setAge(age) {
this.age = age;
}
introduce() {
console.log(`My name is ${this.name} and I'm ${this.age} years old.`);
}
}
const person1 = new Person();
person1.setName("Alice");
person1.setAge(25);
person1.introduce();
この例では、constructor
を使用せずにインスタンス変数を直接クラスに定義し、メソッドを使用して値を設定します。setName
メソッドとsetAge
メソッドを使用して名前と年齢を設定し、introduce
メソッドを使用して情報を出力します。
constructor
を使用しない場合、インスタンス作成後にメソッドを呼び出して値を設定する必要があります。また、インスタンス変数は明示的に宣言する必要があります。
この方法では、初期化処理がconstructor
内に集約されないため、コードの可読性や保守性が低下する可能性があります。constructor
を使用することで、インスタンスの初期化がより明示的かつ一貫した方法で行われるため、一般的には推奨されます。ただし、特定のケースではconstructor
を使用せずにクラスを定義することも可能です。
classの登場
class
は、ECMAScript 2015(またはES6)で導入されました。ES6は、JavaScriptの最新の標準規格であり、2015年にリリースされました。
ES6の導入により、class
キーワードを使用してクラスベースのオブジェクト指向プログラミングがサポートされるようになりました。これにより、クラス、コンストラクタ、メソッド、継承などのオブジェクト指向の概念をより直感的に利用できるようになりました。
それ以前のJavaScriptでは、プロトタイプベースのオブジェクト指向プログラミングが主流であり、クラスの概念はありませんでした。しかし、ES6以前でもオブジェクト指向のパターンを実現するために、関数やプロトタイプを使用する方法がありました。
ES6の導入により、class
構文が追加されたことで、より直感的で明瞭なクラスベースのオブジェクト指向プログラミングが可能となりました。この機能の導入により、JavaScriptの開発者はより効果的にオブジェクト指向のコードを書くことができるようになりました。
classをfunctionに置き換える方法
上記で紹介したように古いやり方で同じことができます。おまけとして理解しておくくらいでOKです。
class
を使用せずにfunction
で同じコードを書き換えると以下のようになります:
function Person(name, age) {
this.name = name;
this.age = age;
console.log("Personインスタンスが作成されました");
}
const person1 = new Person("Alice", 25);
console.log(person1.name); // 出力: "Alice"
console.log(person1.age); // 出力: 25
上記のコードでは、class
の代わりにfunction
を使用してPerson
関数を定義しました。function
の場合、コンストラクタとして使用する関数の名前は大文字で始める慣例があります(パスカルケース)。
Person
関数内部では、this
キーワードを使用してインスタンス変数を初期化します。this
は、新しく作成されるインスタンスを指します。console.log()
文は、インスタンスが作成されたことを示すために表示されます。
インスタンスの作成にはnew
キーワードを使用し、Person
関数に適切な引数を渡します。インスタンス変数には、person1.name
やperson1.age
のようにアクセスできます。
classを使ったモジュールの例
以下は、class
を使用して簡単なモジュールを作成する例です。このモジュールは、数値の加算と乗算を行う機能を提供します。
class Calculator {
add(a, b) {
return a + b;
}
multiply(a, b) {
return a * b;
}
}
export default Calculator;
上記のコードでは、Calculator
というクラスが定義されています。このクラスは、add()
とmultiply()
という2つのメソッドを持っています。add()
メソッドは2つの数値を加算し、multiply()
メソッドは2つの数値を乗算します。
export default Calculator;
の部分は、このモジュールを他のファイルで使用するためにエクスポートしています。このモジュールを他のファイルでインポートすることで、Calculator
クラスの機能にアクセスすることができます。
別のファイルでこのモジュールを使用する例を示します:
import Calculator from './Calculator';
const calc = new Calculator();
console.log(calc.add(2, 3)); // 出力: 5
console.log(calc.multiply(2, 3)); // 出力: 6
上記のコードでは、Calculator
モジュールをimport
文を使用してインポートしています。インポートしたCalculator
クラスのインスタンスを作成し、add()
とmultiply()
メソッドを使用して計算を行っています。
このように、class
を使用してモジュールを作成すると、関連する機能をグループ化し、再利用可能なコードを簡単に作成することができます。
クラスインスタンス変数
クラス内で変数を宣言する際に、let
やconst
などの修飾子を使用しない場合、変数はデフォルトでインスタンス変数として扱われます。インスタンス変数はクラスのインスタンスごとに異なる値を持つことができるプロパティです。
クラスのインスタンス変数と通常の変数の違いを以下の例を使用して説明します。
class Circle {
radius; // クラスのインスタンス変数
constructor(radius) {
this.radius = radius;
}
getArea() {
return Math.PI * this.radius * this.radius;
}
}
const circle1 = new Circle(5); // クラスのインスタンスを作成
console.log(circle1.radius); // 出力: 5
console.log(circle1.getArea()); // 出力: 78.53981633974483
const radius = 10; // 通常の変数
const area = Math.PI * radius * radius;
console.log(area); // 出力: 314.1592653589793
この例では、Circle
クラスと通常の変数を使用して円の面積を計算しています。
Circle
クラスのインスタンス変数radius
は、クラス内で宣言されています。これは各インスタンスごとに異なる値を持つことができます。circle1
インスタンスを作成し、そのradius
プロパティに値5
を設定しています。getArea
メソッドを使用して、radius
プロパティを参照して円の面積を計算しています。
一方、通常の変数radius
は、クラスの外で宣言されています。これは単なる数値の変数であり、特定のオブジェクトやクラスに関連付けられていません。ここでは、radius
変数を使用して直接円の面積を計算しています。
クラスのインスタンス変数は、クラス内のメソッドからthis
キーワードを介してアクセスできます。それぞれのインスタンスは異なる値を持つことができます。
通常の変数は、そのスコープ内でのみ有効であり、クラス内のメソッドから直接アクセスすることはできません。通常の変数は、特定のスコープ内でのみ使用されるデータを表します。
要約すると、クラスのインスタンス変数は、クラスのインスタンスごとに異なる値を持ち、クラス内のメソッドからアクセスできます。一方、通常の変数は、そのスコープ内でのみ有効であり、クラスとは直接関係がありません。
クラスインスタンス変数の代わりにletを使えるか
クラスのインスタンス変数の代わりに let
を使用することもできます。ただし、その場合は以下の点に注意する必要があります。
- スコープの違い:
let
で宣言された変数は、ブロックスコープを持ちます。これは、変数が宣言されたブロック内でのみ有効であることを意味します。一方、クラスのインスタンス変数は、クラス内のどのメソッドからでもアクセスできます。 - インスタンスごとの値の管理:
let
で宣言された変数は、通常の変数と同様に、そのスコープ内でのみ有効です。したがって、クラスのインスタンスごとに異なる値を保持する場合、各インスタンスに対して個別の変数を定義する必要があります。
以下の例を使用して、let
を使用してクラスのインスタンス変数の代わりに変数を宣言する方法を示します。
class Circle {
constructor(radius) {
let localRadius = radius;
this.getArea = function() {
return Math.PI * localRadius * localRadius;
};
}
}
const circle1 = new Circle(5);
console.log(circle1.getArea()); // 出力: 78.53981633974483
const circle2 = new Circle(10);
console.log(circle2.getArea()); // 出力: 314.1592653589793
上記の例では、Circle
クラスのコンストラクタ内で let
を使用して localRadius
変数を宣言しています。localRadius
はクロージャとして getArea
メソッド内で利用されており、各インスタンスごとに異なる値を保持します。
ただし、この方法ではインスタンス変数と同じくらい簡潔でスマートなコードを実現することはできません。クラスのインスタンス変数を使用することで、インスタンスごとのデータの管理やクラスのメソッドからのアクセスがより直感的になります。したがって、通常はクラスのインスタンス変数を使用することが推奨されます。
まとめ
クラスを学ぶことは、オブジェクト指向プログラミングの基礎を理解し、柔軟で効果的なコードを作成するための重要なスキルを身につけることになります。クラスはコードの構造化や再利用性の向上、保守性の向上など、多くのメリットを提供します。是非、がんばりましょう。
お疲れ様でした。