はじめに
最近ちゃんとTypeScript
について勉強し直しまして、
Class
を今までちゃんと使ってこなかったなあと思い、記事にしました。
同じような方の参考になれば幸いです。
Class
の説明をする前にオブジェクト指向について理解しておく必要があるので、簡単に説明します。
オブジェクト指向とは
話題のBingAI
にお願いしました
オブジェクト指向とは、プログラミングの考え方の一つで、現実世界に存在するものを「オブジェクト」として扱い、その特徴や機能を定義することです。オブジェクト指向には、以下のようなメリットがあります。
- オブジェクトを再利用しやすくすることで、開発効率や保守性を高めることができます。
- オブジェクト同士の関係を明確にすることで、設計や理解がしやすくなります。
- オブジェクトの内部構造や動作を隠蔽することで、安全性や信頼性を向上させることができます。
わかりやすいですね。ここで具体例も紹介しておきます。
具体例
とある商品を題材に考えてみます。
その商品には以下のプロパティ(特徴・性質)があるとします。
- String型の名前
- number型の価格
- 名前を変更することができる。
- 商品をカートに入れることができる。
この場合、TypeScriptでは以下のようにオブジェクトを定義することができます。
const goods_A = {
name: 'A商品', // 名前
price: 100, // 価格
rename(n: string): void { // 名前変更
this.name = n;
},
addToCart(): void { // カートに入れる
cart.push(this.name)
}
}
goods_A.name
// 'A商品'
goods_A.rename('a商品')
goods_A.name
// 'a商品'
このようにオブジェクトとして定義し、そのプロパティに「名前」や「価格」や、
「名前変更」や「カートに入れる」といった機能を定義していくプログラミング手法をオブジェクト指向と言います
1. classについて
先ほどの例から考えてみます。
以下のように商品Aがあります。
const goods_A = {
name: 'A商品',
price: 100,
rename(n: string): void {
this.name = n;
},
addToCart(): void {
cart.push(this.name)
}
}
商品が複数件ある場合、
const goods_B = {
name: 'B商品',
price: 100,
rename(n: string): void {
this.name = n;
},
addToCart(): void {
cart.push(this.name)
}
}
const goods_C = {
・
。
・
全ての商品で同じ動きをさせるためには作成分のオブジェクトを定義する必要が出てきますが、
こんなことはしてられません。
オブジェクトの設計書みたいなものがあれば、それを元に増やすことができそうです。
そのオブジェクトの設計書がClassです
先ほどの商品の設計書をClass
を使い定義し、商品のオブジェクトを生成します。
ちなみに、クラスから作成したオブジェクトをインスタンスと言います。
クラスからオブジェクト生成し、解読していきます。
// クラス定義
class Goods {
name: string;
price: number;
constructor(n: string, p: number) {
this.name = n;
this.price = p;
};
rename(n: string): void {
this.name = n
};
addToCart(): void {
cart.push(this.name)
};
}
// インスタンス生成
const goods_A = new Goods('商品A', 100)
goods_A.name
// 'A商品'
2. constructor関数とは
constructor関数とは、クラスで作成されたオブジェクトの初期化のためのメソッドです。
constructor関数は、クラスが呼び出された際に最初に実行されます。
つまり、new Hoge()
の際に実行されるメソッドです。
name: string;
price: number;
constructor(n: string, p: number) {
this.name = n;
this.price = p;
};
先ほど作成したクラスを見ると、
name
をstring
型で定義、
price
をnumber
で定義し、
constructor関数で引数で受け取った値を初期値として格納しています。
new Goods('商品A', 100)
このようにオブジェクトを生成します。
試しに引数に渡している値を変更し、name
にnumber
型をを入れてみます。
このようにエラーになります。
constructor関数の引数の型定義(n: string, p: number)
が、
インスタンス化する時に作用していることが確認できますね。
3. 【TypeScriptのみ】 アクセス修飾子
TypeScriptでは、プロパティのアクセシビリティを制御するため、クラスのプロパティに対してアクセス修飾子public
private
protected
を使うことができます。
class Goods {
owner: string; // 自動的にpublicになります。
private name: string;
public price: number;
protected id: number;
}
- public
- デフォルトはこれ。何も記載がないとpublicになります。
- どこからでもアクセスできる
- private
- 同じクラス内からのみアクセスできる
- protected
- 同じクラスとサブクラス内からのみアクセスできる
なぜ必要なのか
不用意に値を変更できないようにするためです。
次の例で説明します。
class Goods {
public name: string;
constructor(n: string) {
if (n.length < 100) {
this.name = n;
} else {
this.name = '100文字以上'
}
};
rename(n: string): void {
if (n.length < 100) {
this.name = n
}
};
}
このように100文字以下のバリデーションがついたname
プロパティがあったとします。
アクセス修飾子がpublic
の場合
public
のプロパティはどこからでもアクセスできるため、
以下のように、rename
メソッドを通しても通さなくても値を変更できてしまいます。
const goods_A = new Goods('商品A')
// 1. renameメソッドを通す
goods_A.rename('hoge')
// 2. 直接値を変更
goods_A.name = 'fuga'
rename
メソッド内では100文字のバリデーションがかかるのに対し、
直接変更する場合はバリデーションをかけることができません。
アクセス修飾子がprivate
の場合
private
のプロパティは同じクラス内からのみアクセスできるため
以下のように直接指定はできません
この例で不用意に値を変更しないようにアクセスプロパティを適切につける意味が理解できたと思います。
4. 【TypeScriptのみ】 constructor関数の省略記法
さて、次のコードを見て何を思いますか?
class Goods {
name: string;
private price: number;
private store: string;
type: string;
constructor(n: string, p: number, s: string, t: string) {
this.name = n;
this.price = p;
this.store = s;
this.type = t;
};
}
私であれば冗長だなぁ...と思います。
TypeScriptであればこの冗長を解決できます。
それがこちらです
class Goods {
constructor(
public name: string,
private price: number,
private store: string,
public type: string
){};
}
このように初期化処理をかなりシンプルに記述できます。
constructor
関数の引数にアクセス修飾子とプロパティ名を書くだけで自動で初期化処理まで行ってくれます。
アクセス修飾子は必ずつける必要があります。
publicであっても省略することはできません。
ちなみにこのコードをJavaScript
にコンパイルするとこうなります。
class Goods {
constructor(n, p, s, t) {
this.name = n;
this.price = p;
this.store = s;
this.type = t;
};
}
アクセス修飾子がなくなり、全てconstructor
関数でthisの中に格納する形になっていますね。
5. まとめ
- オブジェクト指向とは、現実世界に存在するものを「オブジェクト」として扱い、その特徴や機能を定義すること。
- classとは、オブジェクトの設計図のようなもの。
- constructor関数とは、クラスで作成されたオブジェクトの初期化のためのメソッド。
- TypeScriptでは、不用意に値を変更できないようにするため、クラスのプロパティに対してアクセス修飾子
public
private
protected
を使い、アクセスの制限をかけることができる。 - TypeScriptでは、初期化処理を簡潔にかける省略記法が存在する
他にもinterface
を使ってclassを実装する方法だったり、継承だったり、説明できることはたくさんありますが、今回はここまでの基本的な使い方までとさせていただきます。
続きについてはまた今度書きたいと思います。