JavaScript

JavaScript初心者にclassを伝える

初めに

この記事ではJavascriptのclassについて解説します。
多くの初心者にとってclassは「何だこれ???」と躓くポイントだと思います。
(実際、自分も最初眺めた時は意味が分からず頭が学級崩壊してました。)

なので、本記事ではサンプルコードと共に、
「何だこれ???」を「なるほど!!!」に
変えていけるように解説します。

序章 - 基本構文

まずはclassの基本構文を載せます。
使い方は後々に解説しますので、
とりあえず構文を眺めて美味しいご飯でも考えてください。
意味は深く考えないでいいと思います。

main.js
class NAME {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
}

はいど~ん!
classの基本構文はこんな感じ!これだけ。

大丈夫です、内容も全く難しくないです。
解説すると、

・NAMEは任意の名前(変数や関数の定義と一緒)
・constructorは必須な関数(classが呼び出された時に最初に実行される関数)
・a, bの部分は任意の引数
 →constructorが最初に実行される関数なので、
  引数で初期値を受け渡したりします。
  また、引数の値を保持させるために、
  class自身(this)に保存します。

こういうことです。
これだけなんです。簡単ですね!

第一章 - 例を理解する

さて、構文が把握できた所で簡単な例文を載せます。
怖くないから怖じ気つかないでね。

main.js
class ball {
    constructor(x, y, dx, dy) {
        this.x = x;
        this.y = y;
        this.dx = dx;
        this.dy = dy;
    }
}

var ball_a = new ball(0, 0, 10, 0);

はい。
大丈夫です!簡単です!解説します!

まずclassの中身を見ていきます。
すごく単純です。

引数にはx, y, dx, dyがありますね。
そして、それら全てはthisに保存しています。
はい、classの中身はたったこれだけです。
「引数をthisに保持する」処理しかしていません。

さて、本題は下の変数定義です。

main.js
var ball_a = new ball(0, 0, 10, 0);

これは何を意味しているのでしょうか?

これもまた簡単です。

これはball classの構造を持ったObjectを生成し、変数ball_aに格納する

ことを意味しています。

この「new演算子」とは、Objectを新しく作成する演算子です。
すなわち、この場合classと同じ役割を持った(構造を持った)新しいObjectを生成していることになります。
そして、生成したObjectを変数に代入している、それだけです。

分かりにくいですか?
大丈夫です。簡単です。

試しに

main.js
console.log(ball_a.dx);

こちらを実行するとどうなるでしょうか。
答えは簡単で10(dxの値)がconsoleに表示されます。

なぜなら、変数ball_aには(x, y, dx, dy) = (0, 0, 10, 0)
値を保持したObjectが格納されているからです。

分かりやすく書き換えるとこういう事です。

main.js
// コードA
var ball_a = {
    x: 0,
    y: 0,
    dx: 10,
    dy: 0
};

// コードB
var ball_a = new ball(0, 0, 10, 0);

// コードAとコードBは一緒な処理をしている

こういう事なんですよね。
どうでしょうか、簡単ですよね。

classはこういったObjectの構造を単純化出来て、
一つ定義してあげれば何回でも、同じ構造のObjectが簡単に作れるよ~って事ですね。

例えばめちゃくちゃ複雑なObjectを定義した場合、
毎回毎回、ブラケッツ( {と} )で囲んで記述すると、
可読性が落ちて、デバッグのコストが大変なことになります。

そんな時はObjectの構造体をclassを定義して、
仕様時はnew演算子で定義するほうが楽ですよね。
そうすることによってコードの質も高くなります。

第二章 - ちょっと複雑なもの

恐らく、序章と第一章で全体像を何とな~~く理解できたと思うので、
第二章では実用例を上げながら解説していきます。

ここでの題材は、「シューティングゲームにおける敵の出現の実装」です。

敵を作りたいです。
もちろん、たくさんの敵を。

シューティングゲームを作る際には、
敵を沢山出現させたいですよね。

そんな時にclassはもってこいの存在です。
まず、最初に敵の構造を考えます。

・HPがある
・座標 (x, y) がある
・移動速度 (dx, dy) がある。

これらが敵の構成だとします。
ここまで出来たら次のステップ。実装です。

が、まず最初はあえてclassを使わない実装方法をお見せします。

main.js
var enemies = []; // 敵を格納する配列

enemies[0] = {
    x: 0,
    y: 0,
    dx: 2,
    dy: 0,
    hp: 50
};

enemies[1] = {
    x: 10,
    y: 0,
    dx: 0,
    dy: 2,
    hp: 100
};

こんな形で実装したとします。
ここでは2体の敵を実装しています。
そして、これにあるものを付け足してみます。

main.js
var enemies = []; // 敵を格納する配列

enemies[0] = {
    x: 0,
    y: 0,
    dx: 2,
    dy: 0,
    hp: 50,
    move: function() {
        this.x += this.dx;
        this.y += this.dy;
    }
};

enemies[1] = {
    x: 10,
    y: 0,
    dx: 0,
    dy: 2,
    hp: 100,
    move: function() {
        this.x += this.dx;
        this.y += this.dy;
    }
};

移動をする関数を実装して追記しました。
はい、何か気がついたことはありませんか?

いいですか、私はあなたに点を投げかけますからね?
点と点をつないで線にするのはあなた方次第ですからね?

もしこのまんま、敵が10体、50体...と大量に必要になったらどうでしょうか。
毎回、同じ構造のObjectにもかかわらず、同じコードを大量に書くことになります。
そして100体の処理を定義した後に、構造を変えたいとなったならば、
該当箇所をすべて変更しなければいけないということに陥ります。
悲惨ですね。面倒くさいですね。嫌ですね。
マンネリな人間にはなりたくないですね。

じゃあどうすればいいんですか!!!!って怒る前に聞いてください。
あなたにはclassのご加護がありますよ。

classは全てを解決します。
すなわちclassは救世主。

じゃあclassに置き換えるとどうなるの?
って話ですが、こうなります。

main.js
class enemy {
    constructor(x, y, dx, dy, hp) {
        this.x = x;
        this.y = y;
        this.dx = dx;
        this.dy = dy;
        this.hp = hp;
    }

    move() {
        this.x += this.dx;
        this.y += this.dy;
    }
}

var enemies = []; // 敵を格納する配列

enemies[0] = new enemy(0, 0, 2, 0, 50);
enemies[1] = new enemy(10, 0, 0, 2, 100);

はいqiita━━━━(゚∀゚)━━━━!!
めちゃくちゃシンプルで見やすくなりましたね!
これなら敵が増えてきても、楽ですね。

構造を変えたい!ってなったらclassの中身を弄るだけで済みます。
こうすることで可読性も向上し、デバッグのコストもめちゃくちゃ良くなります。

これが実例をもとに説明するclassを利用するメリットですね。

classを用いると、複雑な構造になったObjectであっても、またそれが
複数利用していようが、一箇所変更するだけで変更できちゃうわけですね。

まとめ

classというのは…

・同じ構造を持ったObjectを複数使用したい時に使う!

つまり…
・複雑なObjectでも誰でも、簡単!一括管理できる!

すると…
・簡略化出来て可読性が上がる!
・デバッグのコストが減る!
・気持ちいい!

というものです!

classを使う時は…

main.js
class NAME {
    // constructorは必須
    constructor(/* 引数 */) {
        /* todo */
    }

    func(/* 引数 */) {
        /* todo */
    }

    /* and more function */
}

var a = new NAME(/* 初期値(パラメータ) */); // 新しくNAMEを作成して変数に代入

このようにして書く!
それだけ、です。
ネ。

終わり

最後まで読んでくださった方ありがとうございます。
文章をまとめるのが下手でグチャグチャした内容、
飛躍した箇所が数多くあったと思います。
分かりにくくて申し訳ないです。

何かご不明な箇所がありましたら、
気軽に質問してください。

それではまた。

(Qiita初投稿)