JavaScriptのクラス(class)を理解する

プログラミング
多くのオブジェクト指向プログラミング言語に備わっている【クラス】構文が、近年JavaScriptにも追加されました。今回はJavaScriptにおけるクラス(class)構文について解説していきます。
JavaScriptのクラス(class)を理解する

多くのオブジェクト指向プログラミング言語には標準的な機能として【クラス】構文が備わっています。
JavaScriptは長らくクラス構文が存在しませんでしたが、ECMASCript2015(2015年版のJavaScriptの仕様)でクラス構文が追加されました。

今回は、JavaScriptのクラス(class)構文について解説していきます。

追加されたJavaScriptのクラス(class)構文

オブジェクト指向と呼ばれるプログラミング言語についての概念があります。
オブジェクト指向とはプログラムを現実のモノのように構成する指向のことです。
このオブジェクト指向の言語として作られたプログラミング言語には、多くの場合クラスという機能が備わっています。

JavaScriptは、オブジェクト指向のプログラミング言語でありながら、このクラス機能が長らく実装されていない、珍しい言語でした。

しかし近年、JavaScriptの新しい標準が改訂されたことにより、JavaScriptにもクラス機能が実装されました。

クラス(class)とは

多くのオブジェクト指向プログラミング言語に備わるクラス(class)という概念について紹介します。
クラス(class)とは、端的にいえばモノの設計書です。
先ほどオブジェクト指向とは、プログラミングを現実のモノのように構成するものだと解説しましたが、クラスとはこのモノのように構造する際の、モノそのものの設計書にあたります。

言葉としては、以下のような意味の対応表になります。

  • オブジェクト = モノ
  • クラス = モノ(オブジェクト)の設計書

クラスモノ(オブジェクト)の設計書であるため、多くのプログラミング言語においてモノ(オブジェクト)を作成する際に必要なものとして定義されることが多いです。

なお、JavaScriptやHTMLのみしか触れたことのない方は、ここで解説するclassがHTMLのclass属性とはまったく異なるものであることに気を付けてください。
JavaScriptを用いてHTMLのclass属性の取得や変更、追加や削除などを行うことがありますが、そういったHTMLを操作する際に登場するclassという言葉は、プログラミング言語の構文としてのclassとは関係のない概念です。

JavaScriptのクラス(class)の特徴

JavaScriptにおいてもクラスがオブジェクト指向におけるモノ(オブジェクト)の設計書であることについては間違いありません。
多くのプログラミング言語と同様のコンストラクタ継承といった概念を備えています。
しかし、特殊なのはその書き方です。
クラス変数はコンストラクタの中でのみ記載できることや、クラス直下にはfunction(メソッド、関数)のみしか配置できないこと当、慣れないと少々書くのが困難なルールがいくつか存在しています。

JavaScriptのクラス(class)の作り方

まず注意すべきことがあります。
JavaScriptにおけるclassは、ECMAScript2015 という近年の新しいJavaScriptの標準として組み込まれた構文です。
そのためInternetExplolerなど一部のブラウザやブラウザのバージョンによっては、この新しい標準への対応が行われおらず、ここで紹介するコードが機能しない可能性があります。
しかし新しい標準のJavaScriptの機能として組み込まれており、徐々に未対応のブラウザが少なくなっているため、現在JavaScriptを学習している方は覚えておいて損はないでしょう。

さて、以上のことを念頭に踏まえた上で解説を進めていきます。
JavaScriptにおける関数の定義には、2つの方法がありました。 ひとつが関数宣言
もうひとつが関数式です。
これと同様に、JavaScriptのクラスの定義にも2つの方法があります。
クラス宣言クラス式です。

クラス宣言

クラス宣言によるクラスの定義は以下のように行います。

class Fooclass {
  // ...クラスの内容をここで定義する
}

関数宣言時に発生するホイスティング(巻き上げ)は、クラス宣言時には発生しません。
そのため、クラスを使用したい場合は必ずクラスを使用する前に、クラス宣言を行うことが必要となります。

クラス式

クラス式によるクラスの定義は以下のように行います。

const fooclass = class {
  // ...クラスの内容をここで定義する
}

クラス式の場合、その名の通り、式としてクラスを定義することができます。 変数に代入するclass式は無名クラスでも名前つきクラスでも可能です。

コンストラクタ(constructor)とメソッド定義

クラス宣言クラス式でクラスを定義する際、そのクラスの中身がどのようなものなのかについて、JavaScriptでは主にコンストラクタ(constructor)メソッドで定義していきます。 コンストラクタは次のように書きます。

const fooclass = class {
  constructor(x, y) { /* コンストラクタ */
    this.x = x
    this.y = y
  }
}

constructor()という名前のメソッドがコンストラクタと呼ばれるものです。
コンストラクタは定義したクラスからオブジェクトを生成し、初期化する際に実行される特殊な初期化用メソッドです。
constructor()内でthis.xにconstructor()の第一引数xを代入していますが、これは自身のプロパティxに指定された引数の値を設定するという意味になります。
これにより、オブジェクトが自らのプロパティとその値を設定できるようになります。

また、コンストラクタとは別に通常のメソッドもクラスの内容として定義することが可能です。
コンストラクタの他にメソッドも定義する場合、以下のようなコードになります。

const fooclass = class {
  constructor(x, y) { /* コンストラクタ */
    this.x = x
    this.y = y
  }
calc() {  /* メソッド */
    return this.x + this.y  /* x と y を足した値を返却する */
  }
}

上記コードのcalc()がクラス内でのメソッド定義になります。
ここでは、fooclassクラスのプロパティ x と y を足した値を返すメソッドとして定義しています。
このようにクラス内でコンストラクタとメソッドを定義することで、クラスからオブジェクトを作成した際に、定義したコンストラクタのプロパティと、メソッドを使用することができるようになります。

JavaScriptのクラス(class)の使い方

クラスはクラス宣言クラス式によって定義しただけでは役に立ちません。
クラスはオブジェクト指向におけるモノの設計書であり、役割は設計したモノを生成することです。
つまり基本的には、オブジェクトを作成してはじめて、クラスという概念が役に立つといえるでしょう。

(1)オブジェクトの作成(new)

クラスから、クラスのインスタンスであるオブジェクトを作成するためにはnew演算子を用いる必要があります。
new演算子を用いたオブジェクトの作成は、次のように行います。

var foo1 = new fooclass(10, 20)

これでfooclassのインスタンス(オブジェクト)が作成され、変数foo1に代入されました。
new演算子 + クラス + 括弧でオブジェクトが作成されるのです。 この際に登場する括弧は、クラス定義の際に登場したコンストラクタ(constructor)に渡される引数を示します。

(2)プロパティの取得と設定

クラスのプロパティは以下のように呼び出すことが可能です

var hooclass = class {
  constructor(z) {
    this.z = z
  }
  get getZ() {
    /* get構文 */
    return this.z
  }
}
var hoo = new hooclass(20)
var val = hoo.getZ
console.log(val) /* 20が出力される */

クラス定義の中にgetという構文があります。
このget構文を用いることで、オブジェクト作成後、get構文で指定したメソッドの返却値を取得できます。

また値を取得するget構文に対し、値を設定するset構文というものが存在します。

var hooclass = class {
    constructor(z) {
        this.z = z
    }
    get getZ() {
        return this.z
    }
    set setZ(z) {
        /* set構文 */
        this.z = z
    }
}
var hoo = new hooclass(20)
hoo.setZ = 100 /* プロパティに値を設定する */
var val = hoo.getZ
console.log(val) /* 100が出力される */

set構文で指定された上記コード中の valueOfZ() メソッドを使用すると、インスタンス作成後に、そのインスタンスのプロパティに値を設定することができます。
ここでははじめ、コンストラクタによりプロパティ z を 20 で初期化したインスタンスが、set構文で指定したメソッド valueOfZ に 100 を渡すことで、プロパティ z を 100 に再設定しています。

(3)継承

オブジェクト指向における代表的な概念のひとつに継承があります。
継承とは、クラス間に親子関係を作り出し、親のクラスが保有するメソッドやプロパティを使用できるようにするものです。
継承を行うには extends を利用します。

var hooclass = class {
    constructor(z) {
        this.z = z
    }
    get getZ() {
        return this.z
    }
    set setZ(z) {
        /* set構文 */
        this.z = z
    }
}
/* hooclassの継承 */
var taaclass = class extends hooclass {
    constructor(z, num) {
        super(z) /* 親クラスのコンストラクタ */
        this.num = num
    }
    calc() {
        return this.z * this.num
    }
}

var taa = new taaclass(20, 3)
taa.setZ = 100 /* 親クラスのメソッドでプロパティに値を設定する */
console.log(taa.calc()) /* 300が出力される */

クラス作成時、上記コードのように extends + “継承したいクラス名” で親クラスを指定することが出来ます。
継承する親クラスのプロパティを初期化するために、constructor には親クラスのコンストラクタを示す super() の引数に、初期化したい値を指定する必要があります。

(4)静的メソッド

クラスのインスタンスではなく、クラスそのものから呼び出しを行う静的メソッドというものが存在します。

var hooclass = class {
    static writeDescription() {
        /* staticをつけることで静的メソッドを定義できる */
        return 'これが静的メソッドです。'
    }
}
console.log(hooclass.writeDescription()) // 静的メソッド実行

上記コード中のwriteDescription()メソッドの定義箇所を見てみましょう。
メソッド名の前に static とあります。
これは、ここで定義するメソッドが静的メソッドであることを示します。
静的メソッドはクラスのインスタンスではなく、クラスそのものから呼び出しを行うため、クラス名.静的メソッド名() のみで実行することができます。

まとめ

JavaScriptで新しく追加されたclass構文を用いることで、オブジェクト指向としてさらに可読性の高いコードを書くことができるようになります。
クラス式・クラス宣言によるクラス定義と、new演算子によるクラスのインスタンス(オブジェクト)の作成、親クラスの継承や静的メソッドをうまく活用して、より高い可読性のコードを実装していきましょう。


TECH PLAYでは、エンジニアの方向けに勉強会・イベント情報を提供しています。
ご興味のある方はぜひご参加ください。


おすすめのコラム