#はじめに
最近、プログラミング初学者の天敵(?)である「クラス」をようやく退治する気になりました。
という訳で、本記事ではTypescriptにおける「クラス」について基礎から探っていきます。
※執筆者である私は、プログラミング初学者です。そのため扱うレベルが低く、内容に誤りが含まれている可能性もありますが、ご容赦下さい。
##クラスとは?
JavascriptやTypescriptにおいて、オブジェクトを生成する方法は2つあります。
①オブジェクトリテラル
②クラス
①のオブジェクトリテラルは、直接オブジェクトを生成する方法です。
//こんな感じ
const point = {
x: 1,
y: 2
}
一方、クラスとはオブジェクトの設計図のようなものです。
設計図を用意しておき、必要な場面でオブジェクトを生成します。
同じようなオブジェクトが複数必要な場合は、クラスを用いると便利です。
##クラスの定義
一例ですが、クラスは下記のように定義されます。
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
classというキーワードを使って、クラスが定義されています。
何か色々と見慣れないものが出てきましたが、まずこの部分に着目します。
var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
定義されたクラスを、上記の記述によって、オブジェクトを作成しています。
クラスから作成されたオブジェクトは、「インスタンス」と呼ばれます。
仮にクラスを使用しないとなると、p1やp2といったオブジェクト毎に記述が必要になってしまいます。
クラスの便利さが何となく分かってきました。
console.log()でオブジェクトを確認しておきます。
console.log(p1)
console.log(p2)
console.log(Point)
//結果 下記のようなオブジェクトが出来上がる。
{
"x": 0,
"y": 10
}
{
"x": 10,
"y": 20
}
##コンストラクタ
次は、下記部分を理解していきます。
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
これは「コンストラクタ」と呼ばれるメソッドです。コンストラクタは、newでインスタンス(オブジェクト)を作成する際に、最初に呼び出される特別なメソッドです。
コンストラクタは、オブジェクトの作成に必要な「引数」を渡す役割を担います。
クラスは設計図です。そこに、データ(引数)を渡すことで、個別のインスタンス(オブジェクト)が作成されます。
データ(引数)は、コンストラクタというメソッドを用いて、クラスに渡されます。
var p1 = new Point(0, 10); //0,10 がコンストラクタメソッドの引数に渡される。
var p2 = new Point(10, 20); //10,20がコンストラクタメソッドの引数に渡される。
###コンストラクトを用いないと?
コンストラクタを用いない場合、どういう記述になるか気になりました。
こんな感じでしょうか?
class Point {
x: number;
y: number;
}
var p1 = new Point();
p1.x = 0;
p1.Y = 10
var p2 = new Point();
p2.x = 10;
p2.Y = 20;
コンストラクタを用いたほうが、簡単にインスタンスが作成できそうです。
クラスにメソッドを追加する
クラスにはメソッドを追加することが出来ます。
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
//functionは省略して記述される。
showNumber() {
console.log(`x:${this.x}, y:${this.y}`);
}
//function showNumber() {
// return new Point(this.x + point.x, this.y + point.y);
// }
}
var p1 = new Point(0, 10);
//メソッドを呼び出す。
p1.showNumber()
クラス内の値を参照する時は、thisを用います。thisの挙動については、非常に面白いので後述します。
###クラス内のメソッドの活用
クラス内のメソッドは、例えば下記のように活用することができます。
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
add(point: Point) {
return new Point(this.x + point.x, this.y + point.y);
}
}
var p1 = new Point(0, 10);
var p2 = new Point(10, 20);
var p3 = p1.add(p2); // {x:10,y:30}
難しいですね。
ちなみにこれらのコードは、公式ドキュメントから拝借しています。
流れとしては、こんな感じでしょうか。
①p1,p2オブジェクトの作成
②p1オブジェクトの、addメソッドを呼び出す。その際、引数としてp2オブジェクトが渡される。(p1.add(p2)
の部分)
③addメソッドは、新しいインスタンス(オブジェクト)を作成する(返す)。
そして、p3をconsole.log()で確認すると、{x:10,y:30}
が出力される。
p1{x:0,y:10}
にp2{x:10,y:20}
が加算され、{x:10,y:30}
になっています。
###クラスを定義すると、自動で型が生成される。
この部分に着目してみて下さい。
//引数に型Pointが指定されている。
add(point: Point) {
//省略
}
引数にPoint型が指定されています。
Point型がいつ定義されているかというと、クラスを定義したタイミングです。
クラスを定義したタイミングで、そのクラスの型が自動で作成されます。
この場合Point型は、x,yプロパティ、addメソッドを持つオブジェクトです。
add(point: Point)
において、addメソッドの引数には、Point型しか渡せないという指定になっています。
##thisの挙動
ここからは少々マニアック?な話になります。
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
showNumber() {
console.log(`x:${this.x}, y:${this.y}`);
}
}
var p1 = new Point(0, 10);
p1.showNumber()
//結果
"x:0, y:10"
上記のようなコードを、以下のように書き換えてみます。
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
showNumber() {
console.log(`x:${this.x}, y:${this.y}`);
}
}
var p1 = new Point(0, 10);
const anotherP1 = {
anotherShowNumber: p1.showNumber
}
anotherP1.anotherShowNumber()
//結果
"x:undefined, y:undefined"
undefinedになってしまいますね。
オブジェクトの中でthisを使用した場合、thisはそのオブジェクトを示します。
つまりこの場合のthisは、anotherP1を参照しているのです。
例えばこのように記述してみます。
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
showNumber() {
console.log(`x:${this.x}, y:${this.y}`);
}
}
var p1 = new Point(0, 10);
const anotherP1 = {
x: 100, //追加
y: 200, //追加
anotherShowNumber: p1.showNumber
}
anotherP1.anotherShowNumber()
//結果
"x:100, y:200"
thisの使用についてはまだまだ追求できそうですが、とりあえずここまでにしておきます。
興味のある方は、class内のメソッドをアロー関数で記述したらどうなるか、試してみると面白いです。
#最後に
長くなったので、続きは別の記事にしておきます。
次回以降は、クラスの継承やメソッドのオーバーライド、アクセス修飾詞、setterとgetterについて触れていきます。
###参考
TypeScript Deep Drive 日本語版
超TypeScript入門 完全パック(2021)
TypeScriptハンズオン