以前オブジェクト指向とは?に関して大枠を掴むための記事を書きました。
オブジェクト指向とは?初心者でもわかりやすく徹底解説!
プログラミングをする上で、様々な書き方・概念がある中、必ず直面するのが「オブジェクト指向」というものです。 Java・Ruby・Python・C#等々言語に関係無くオブジェクト指向の概念は存在し、しか ...
続きを見る
今回は、オブジェクト指向で実際に書いてみて内容をより深く知るための記事です。
JavaScriptを対象として記載するので、JavaScriptによるオブジェクト指向の書き方を学びたい人にも参考になるかと思います。
ちなみに、前回の記事はこちらです。
前回の記事でも書きましたが、オブジェクトの基本用語として
・オブジェクト(車)
・クラス(設計図)
・プロパティ(部品)
・メソッド(動作)
・インスタンス(実体)
というものがありました。(前回同様車を例に使用しております。)
それと紐付けて解説していきたいと思います。
また、ES2015以前と以後の流れに関しても書いておりますので、「ES2015までの書き方は詳しいけど導入されてからの書き方にはちょっと不安」と言った方にも参考になるかと思います。
目次
クラスベース言語とプロトタイプベース言語
書き方の説明をする前に、オブジェクト指向言語の種類に関してお話しなければなりません。
※「既にこの部分は知っているよ」という方や、「JavaScriptにはES2015でクラス構文が導入された話」を知っている方は、「JavaScriptによるオブジェクト指向の書き方」まで読み飛ばして頂いて構いません。
オブジェクト指向のプログラミング言語は、「クラスベース言語」と「プロトタイプベース言語」に分類されます。
例えばクラスベースは、JavaやC++等の言語が対象で、プロトタイプベースはJavaScriptやRuby等の言語が対象です。
クラスベースとプロトタイプベースの違い
クラスベースでは、クラスとインスタンスという概念がありますが、プロトタイプベースはプロトタイプオブジェクト(Prototypical Object)があるのみとなります。
クラスベースでは、全てのオブジェクト(車)はクラス(設計図)から作成されます。
クラス(設計図)からオブジェクト(車)を作成する流れをインスタンス(実体)化と言います。
一方でプロトタイプベースでは、クラス(設計図)に該当するものは存在しません。
そこで、先ほど登場したプロトタイプオブジェクトの出番です。
プロトタイプベースでは、クラス(設計図)の代わりに、このプロトタイプオブジェクトがクラス(設計図)の役割をします。
何やら色んな単語がずらずらと並んでしまいましたが、プロトタイプベースは全て「オブジェクト」である。とシンプルに理解しておくのが良いかもしれません。
具体的な書き方の違い
クラスベースではオブジェクト(車)の設計図としてクラスを使用されていますが、ルール上クラスの定義は一度しかできないので、後から変数やメソッド(処理)を追加できないのです。
一方でプロトタイプベースでは、設計図としてプロトタイプオブジェクトというオブジェクトを使用しており、オブジェクトは動的なものなので、後から変数やメソッド(処理)を追加できる柔軟性を持っているのです。
どちらが良いかというのは、見方によって異なるかと思います。
設計図として一度確定させたものを変更できないよう制約を設けておいた方が良いケースもありますし、後から柔軟に変更していく開発スタイルの方が良いケースもあります。
メリットデメリットに関しては、実際に触ってみて気付く部分も非常に大きいので、ひとまずここでは、「プロトタイプベースでは後から設計図を色々変えたりすることが可能なんだな」くらいに理解しておいて下さい。
JavaScriptの変異
ここまで、クラスベースとプロトタイプベースに関してお話しました。
「なるほどーJavaScriptはクラスが存在しないと考えて書いていけばいいんだな。」と思うところではあるのですが、実は2015年にとある変革が起こりました。
ES2015と呼ばれるものの登場です。
ES2015
ES2015の名称由来は、ECMAScript 6th editionが2015年に標準化したところに起因します。
「ES6」と表現するケースもありますが、どちらも間違いではありません。
今までは、クラスの概念が存在しなかったプロトタイプベースであるJavaScriptに、ついに「クラス構文」が導入されることになります。
プロトタイプベースなのにクラス構文が導入されるというところが、世間のJavaScriptのややこしい部分に繋がっているわけでもあるのですが、クラス構文が導入されたことで、JavaScriptのオブジェクト指向の書き方が一気に変わることになります。
もちろん今までの書き方でも動作はするのですが、現代の主流に合わせた書き方はES2015以降のものとなります。
さて、ここまでの前提知識を踏まえた上で、いよいよJavaScriptによるオブジェクト指向の書き方に入りたいと思います。
リセマラが気になる方はこちらも!
JavaScriptによるオブジェクト指向の書き方
様々な単語が登場しましたが、順を追って基本・根幹となるところから進んで行きたいと思います。
基本的な流れとしては、オブジェクトを作成し、その中にプロパティを付与していく流れとなります。
例のごとく「車」を使って記述してみましょう。
オブジェクトの作成及びプロパティの付与
//新規オブジェクト作成
var car = new Object();
//プロパティ(「車の名前」・「最大速度」)
car.name = "レクサス";
car.maxspeed = 250;
console.log(car.name + "最大" + car.maxspeed + "kmで走ります。");
オブジェクトの作成は、new Object()を用いて作成し、そこに部品部分となるプロパティを付与させていく形になります。
上記の出力結果は、「レクサスは最大250kmで走ります。」となります。(なぜレクサスを選んだのかとかは置いておいて下さい。笑)
上記では、carというオブジェクトに対して「名前」と「最大速度」の二つのプロパティしか付けておりませんが、オブジェクトにはメソッド(処理)を付与することも可能です。
オブジェクトに対するメソッド付与
var car = new Object();
car.name = "レクサス";
car.maxspeed = 300;
//特徴を述べるメソッド(処理)
var feature = function() {
console.log(car.name + "最大" + car.maxspeed + "kmで走ります。");
};
//特徴を出力する
car.feature();
このように記述しますと、以降レクサスの特徴を出力したいときは、
car.feature();
と記述するのみでOKです。
わざわざ毎回、
console.log(car.name + "最大" + car.maxspeed + "kmで走ります。");
と書く必要はないんですね。
ただ、勘の良い人は気付いたかもしれません。
記述の中でやたらと使われている単語がありますね?
そう、「car」です。
よく国語の授業でも、「これ」が指す言葉は何でしょう?みたいな問題がありましたよね。
例えばこのcarオブジェクトの中での「これの名前」と出てきたら、どう考えても「carの名前」なんだなって理解できるので、carは全て「this」と表記することが可能です。(今回はcarの方が文字数的には短い形になっておりますが、長いオブジェクト名のも開発していると必ず出てくるので、よりコード数を短縮して書くことができます。)
以上のことから、上記のコードは下記のように書き換えることが可能です。
オブジェクト内プロパティに付与される物の「this」表記
var car = new Object();
//「car」の部分を「this」に変更
this.name = "レクサス";
this.maxspeed = 300;
var feature = function() {
console.log(this.name + "最大" + this.maxspeed + "kmで走ります。");
};
car.feature();
thisはもはや当然の機能としてあらゆるコードで見かけることになりますので、必ず覚えておきましょう!
さて、現在車は1台しかありません。
開発をしていると、車の数は当然増えて行きます。
例えば車が3台になったらどうなるでしょうか。
複数オブジェクトに対するメソッドの使い回し
//1台目
var carOne = new Object();
this.name = "レクサス";
this.maxspeed = 300;
//2台目
var carTwo = new Object();
this.name = "ベンツ";
this.maxspeed = 250;
//3台目
var carThree = new Object();
this.name = "プリウス";
this.maxspeed = 200;
var feature = function() {
console.log(this.name + "最大" + this.maxspeed + "kmで走ります。");
};
//各車の特徴を出力する
carOne.feature();
carTwo.feature();
carThree.feature();
この場合、レクサス・ベンツ・プリウスがそれぞれの特徴を出力したいときは、外に出されているfeature()をそれぞれのオブジェクトで呼ぶだけでOKです(記述内の、「各車の特徴を出力する」という部分です。)
毎回、
console.log(this.name + "最大" + this.maxspeed + "kmで走ります。");
を書く必要無く、共通部分として外出ししたおかげで「各車の特徴出力」はシンプルなものになりましたが、もう少し紐解いて見ると、全て同じ「車」なのだから、さらに統一的な記述をしたいですよね。
ここで、冒頭でも説明したES2015以降で搭載された、「クラス構文」の登場です。
リセマラが気になる方はこちらも!
コンストラクタ
コンストラクタとは、クラスのインスタンス生成時に実行されるメソッドであり、クラス内のメンバ変数の初期化をするときに呼び出します。
メンバ変数というのは、プロパティの部分です。
先ほどの例ですと、「車の名前」と「車の最大速度」です。
厳密には、「クラス構文」がJavaScriptでは使えるようになっただけで、本質的にはプロトタイプベース言語なので、あくまでもコンストラクタ(のようなもの)という位置付けになります。
早い話がコンストラクタは関数のようなもので、今回だと車のコンストラクタを作成するとなると、まず下記の頭に出てくる関数を用意し、以降はそれを用いて3台分の記述を用意します。
コンストラクタ(のようなもの)の使い方
//コンストラクタ(のようなもの)
function Car(name,maxspeed) {
this.name = name;
this.maxspeed = maxspeed;
this.feature = feature;
}
var feature = function() {
console.log(this.name + "最大" + this.maxspeed + "kmで走ります。");
};
//インスタンス
var carOne = new Car("レクサス",300);
var carTwo = new Car("ベンツ",250);
var carThree = new Car("プリウス",200);
carOne.feature();
carTwo.feature();
carThree.feature();
Carの関数の()内にあるnameとmaxspeedは引数と言って、他の記述部分でそれぞれの値を入れると、関数内の対応した変数に格納され処理されます。
例えば、
new Car("レクサス",300);
の記述は、
function Car("レクサス",300) {
this.name = "レクサス";
this.maxspeed = 300;
this.feature = feature;
}
と、内部では処理されております。
また、コンストラクタではprototypeというプロパティを持っております。(オブジェクト)
コンストラクタのprototypeが保有するプロパティ・メソッドはインスタンスのプロパティ・メソッドとして参照することが可能となります。
コンストラクタのprototypeに対する意味・参照方法
function Car(name,maxspeed) {
this.name = name;
this.maxspeed = maxspeed;
this.feature = feature;
}
//Car.prototypeにfeatureメソッド付与
Car.prototype.feature = function() {
console.log(this.name + "最大" + this.maxspeed + "kmで走ります。");
};
//インスタンス
var carOne = new Car("レクサス",300);
var carTwo = new Car("ベンツ",250);
var carThree = new Car("プリウス",200);
carOne.feature();
carTwo.feature();
carThree.feature();
上記の書き方をすることで、Carのprototypeに特徴を紹介するfeatureメソッドが付与されました。
carOne~carThreeは全てCarのインスタンスですので、Carのprototypeにあるfeatureメソッドを使用することができます。
ここまでで、コンストラクタを用いてクラス(のようなもの)を作成する流れを説明しました。
上記のコーディングは更にシンプル化させることが可能なのですが、その上で非常に重要な考え方である「継承」に関して説明する必要があります。
記事自体が長くなってきましたので、一旦ここで止めて、次回の記事で「継承」に関して説明していきたいと思います。
まずはこの記事で、下記の内容を理解して貰えたらと思います。
①オブジェクトの作成及びプロパティの付与
②オブジェクトに対するメソッド付与
③オブジェクト内プロパティに付与される物の「this」表記
④複数オブジェクトに対するメソッドの使い回し
⑤コンストラクタ(のようなもの)の使い方
⑥コンストラクタのprototypeに対する意味・参照方法