🔥

【JS】クラスについて解説

2021/10/03に公開
5

今回は、JavaScriptにおけるクラスについて解説していきます。

クラスとは、動作や状態などの構造を定義した設計図のようなものです。

この設計図を使って、同じ性質を持ったインスタンスを作ることができます。

あまり詳しくクラスについては説明しないので、詳細は各自で調べていただければと。

クラスの定義


クラスの定義の仕方は以下のようになります。


class MyClass {
  
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.z = 1;
  }

  increment() {
    this.x++;
  }

}

const myClass = new MyClass(2, 3);

// 2
console.log(myClass.x);
// 3
console.log(myClass.y);
// 1
console.log(myClass.z);
myClass.increment();
// 3
console.log(myClass.x);

constructorで初期化処理を書くことができ、thisはインスタンス自身を指します。

そして、クラスはnew演算子でインスタンスを作成できます。

ちなみに、JavaScriptでは慣習としてクラス名には大文字ではじまる名前をつけます。

メソッド


クラスにおけるメソッドの種類は主に3つあります。

それは以下の通り。

名前 説明
プロトタイプメソッド インスタンスが共通で持つメソッド
インスタンスメソッド インスタンス自身が持つメソッド
スタティックメソッド 外から参照できるメソッド

コードにすると以下のようになります。


class MyClass {
  constructor() {
    this.method2 = () => {
      // インスタンスメソッド
      console.log("インスタンスメソッドです”);
    };
  }
  increment() {
    // プロトタイプメソッド
    this.x++;
  }
  static method() {
    // スタティックメソッド
    console.log("スタティックメソッドです”);
  }
}

const myClass = new MyClass();
// インスタンスメソッドです
myClass.method2();
// スタティックメソッドです
MyClass.method();

ちなみに、同じ名前のインスタンスメソッドとプロトタイプメソッドがある時は、インスタンスメソッドが優先して呼ばれます。

ただインスタンスメソッドとプロトタイプメソッドの使い分けなどは、正直イマイチわからなかったので、わかる方はコメントいただければと、、

継承


extendsキーワードを使うことで、クラスを継承することができます。

継承とは、動作や状態などを引き継いだ新しいクラスを定義することです。

そして、継承する上で知っておくべきことがsuperです。

superを使うことで、親クラスのコンストラクターを呼び出すことができます。


class MyClass {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.z = 1;
    this.method2 = () => {
      console.log("インスタンスメソッドです");
    };
  }
  increment() {
    // `this`は`Counter`のインスタンスを参照する
    this.x++;
  }
  static method() {
    // 静的メソッドの処理
    console.log("スタティックメソッドです");
  }
}

class ChildClass extends MyClass {
  constructor(x, y, z) {
    super(x, y);
    this.a = z;
  }
}
const childClass = new ChildClass(1, 2, 3);

// 1
console.log(childClass.x);
// 2
console.log(childClass.y);
// 1
console.log(childClass.z);
// 3
console.log(childClass.a);
childClass.increment();
//2
console.log(childClass.x);
// インスタンスメソッドです
childClass.method2();
// スタティックメソッドです
ChildClass.method();

オブジェクト指向ではこの継承はよく使われるので、覚えておいていただければと。

おわりに


クラスについて説明してきました。

今回は割と抽象的な話になってしまったので、少し難しかったかもしれないです。

機会があれば、クラスの具体的な活用方法などについても書こうと思います。

最後に宣伝です。

0からエンジニアになるためのノウハウをブログで発信しています。
https://hinoshin-blog.com/

また、YouTubeでの動画解説も始めました。
YouTubeのvideoIDが不正ですhttps://www.youtube.com/channel/UCqaBUPxazAcXaGSNbky1y4g

インスタの発信も細々とやっています。
https://www.instagram.com/hinoshin_enginner/

興味がある方は、ぜひリンクをクリックして確認してみてください!

おわり

Discussion

standard softwarestandard software

ただインスタンスメソッドとプロトタイプメソッドの使い分けなどは、正直イマイチわからなかったので、わかる方はコメントいただければと、、

下記のコードでわかるでしょうか。

インスタンスメソッドとスタティックメソッドの違いは基本だと思いますので調べてみてください。
プロトタイプメソッドというのはまた別です。

class MyClass {
  constructor(name) {
    this.name = name
  }
  
  instanceMethod() {
    console.log('インスタンスメソッド:' + this.name)
  }
  
  static staticMethod() {
    console.log('スタティックメソッド')
  }
}

const myClass1 = new MyClass('マイクラス1');
myClass1.instanceMethod(); // "インスタンスメソッド:マイクラス1"

// myClass1.staticMethod(); // エラーになる

console.log(myClass1.toString()) // [object Object]
// toStringがプロトタイプメソッドというものです。

const myClass2 = new MyClass('マイクラス2');
myClass2.instanceMethod(); // "インスタンスメソッド:マイクラス2"

MyClass.staticMethod(); // "スタティックメソッド"
ShinyaHinoharaShinyaHinohara

コメントありがとうございます!

すみません、言葉の定義が曖昧でしたね、、

本記事ではconstructorの中でメソッドを定義しているものをインスタンスメソッド、
standard softwareさんがインスタンスメソッドと呼んでいるものの定義の仕方をしたものをプロトタイプメソッドと呼んでいます。

この言葉の定義をした上で、インスタンスメソッドとプロトタイプメソッドに挙動の違いがあるのかを知りたかったです。

standard softwarestandard software

すいません。私も構文の日本語名称が適当で間違いました。
プロトタイプメソッドというのは、プロトタイプ継承されたものかと思いましたが、違うんですね。

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Classes#プロトタイプメソッド
このあたりをみると、
「プロトタイプメソッド」「静的(スタティック)メソッド」がありますね。

インスタンスメソッドと、プロトタイプメソッドは、何か違うのかなと、調べました。
下記コードでみると、
instanceMethod1 と prototypeMethod は、call で数値呼び出しに対してthisがnumberを返すようになっていて似ています。

また、nameプロパティにメソッド名が入るようです。

Reactとかだと、prototypeMethod だと、bind(this)しないといけないかもですが、
instanceMethod2の書き方だと、thisが固定されて、bind(this)しなくてもいいかも。よくわからないです。

class MyClass {
  constructor(name) {
    this.instanceMethod1 = function() {
      console.log('インスタンスメソッド1:' + typeof this)
    }
    this.instanceMethod2 = () => {
      console.log('インスタンスメソッド2:' + typeof this)
    }
  }
  
  prototypeMethod() {
    console.log('プロトタイプメソッド:' + typeof this)
  }
  
  static staticMethod() {
    console.log('スタティックメソッド:' + typeof this)
  }
}

const myClass1 = new MyClass('マイクラス1');

myClass1.instanceMethod1();                 // "インスタンスメソッド1:object"
console.log(myClass1.instanceMethod1.name); // ""
myClass1.instanceMethod1.call(1);           // "インスタンスメソッド1:numbe  

myClass1.instanceMethod2();                 // "インスタンスメソッド2:object"
console.log(myClass1.instanceMethod2.name); // ""
myClass1.instanceMethod2.call(1);           // "インスタンスメソッド2:objec  

myClass1.prototypeMethod();                 // "プロトタイプメソッド:object"
console.log(myClass1.prototypeMethod.name); // "prototypeMethod"
myClass1.prototypeMethod.call(1);           // "プロトタイプメソッド:number  

MyClass.staticMethod();                     // "スタティックメソッド:function"
console.log(MyClass.staticMethod.name);     // "staticMethod"
MyClass.staticMethod.call(1);               // "スタティックメソッド:number"
cisdurcisdur

期間の空いたコメントですが返信させていただきます。

class MyClass {
  constructor() {
    this.instanceMethod = function() {
      console.log('インスタンスメソッド')
    }
  }
  
  prototypeMethod() {
    console.log('プロトタイプメソッド')
  }
}

const myObj1 = new MyClass()

上記コードの場合、instanceMethodmyObj1が直接持つプロパティ(メソッドもプロパティの一種)です。一方、prototypeMethodmyObj1が直接持っているわけではなく、myObj1のプロトタイプが持つプロパティとなります。

このことは、Object.hasOwnを用いて確かめられます。

// 前のコードの続き

console.log(Object.hasOwn(myObj1, 'instanceMethod')) // -> true
console.log(Object.hasOwn(myObj1, 'prototypeMethod')) // -> false
console.log(Object.hasOwn(Object.getPrototypeOf(myObj1), 'prototypeMethod')) // -> true

実用上の違いとしては、instanceMethodMyClassのインスタンスを作成するたびに(関数オブジェクトとして)生成されるのに対し、prototypeMethodMyClassのすべてのインスタンスで共通の関数を参照している、ということが挙げられます。

// 前のコードの続き

const myObj2 = new MyClass()

console.log(myObj1.instanceMethod === myObj2.instanceMethod) // -> false
console.log(myObj1.prototypeMethod === myObj2.prototypeMethod) // -> true
console.log(myObj1.prototypeMethod === MyClass.prototype.prototypeMethod) // -> true

これにより、MyClassのインスタンスを大量に作成しても、prototypeMethodが使用するメモリの量を抑えられます。

原則として、インスタンスのメソッドを定義する場合、この記事でいう「プロトタイプメソッド」のスタイルで行うべきでしょう。