ミス等有りましたらご助言いただけますと幸いです。
#コンストラクタ
オブジェクトを生成するには、 {}
のようなオブジェクト初期化子を使用するか、コンストラクタ関数を用いる。
コンストラクタ関数はオブジェクトの雛形(種類)を定義する。
定義したら、new を用いてそのオブジェクトのインスタンスを作成する。
function Task(name) {
this.name = name;
this.isDone = false;
}
const task = new Task('a');
#プロトタイプ
オブジェクトはprototype
オブジェクトという特別なプロパティを持っている。
コンストラクタ関数と併せて使用し、インスタンス化した際に__proto__
にprototype
の参照がコピーされる。
// ES6以前
function Task(name) {
this.name = name;
this.isDone = false;
}
Task.prototype.alertName = function(){
alert(this.name);
};
const task = new Task('a');
task.alertName();
prototypeを使って既存のコンストラクタにメソッドを追加でき、インスタンス化したオブジェクトから呼び出すことが可能。
下記のように、メソッドを定義しないのはメモリの節約(インスタンス化するたびに定義される)のため。
function Task(name) {
this.name = name;
this.isDone = false;
this.alertName = function() { ~~ };
}
#プロトタイプチェーン
どのオブジェクトもプロトタイプと呼ばれる、他のオブジェクトへの内部的な繋がりを持っている。
そのプロトタイプオブジェクトもプロトタイプを持っており、あるオブジェクトのプロトタイプが null に到達するまでそれが続く。
つまり、プロトタイプが多階層になっていること。
例えば、下記を実行する。
const task = new Task('a');
task.hasOwnProperty('name');
hasOwnProperty
というメソッドは、Task
オブジェクトは持っていないし、プロトタイプに定義もしていないので、
更に下のprototype オブジェクト (Objectのprototype) のhasOwnProperty
を実行する。
優先順位は、コンストラクタ内に定義したものが一番最初、次にプロトタイプ、次にプロトタイプのプロトタイプ...となる
- プロパティの値がローカルに存在するかを確かめ、存在している場合は、その値を返します。
- 値がローカルに存在していない場合は、プロトタイプチェーンを確認します(proto プロパティを使用)。
- プロトタイプチェーン内のオブジェクトが指定したプロパティの値を持っている場合は、その値を返します。
- そのようなプロパティが見つからない場合は、オブジェクトにそのプロパティは存在しません。
#プロトタイプを使った継承
プロトタイプチェーンを使って、プロトタイプオブジェクトを下ってプロパティを使用できることがわかったので、これを用いて継承をしていく。
タスクオブジェクトのもととなるコンストラクタ関数を下記のように定義する。
function Task(name) {
this.name = name;
this.isDone = false;
}
Task.prototype.alertName = function() { alert(this.name); };
これを継承し、期限を設定できるタスクを作成したい。ついでにそれをアラートするメソッドも
まずは、同様に作成してみる。
function LimitTask(name, limit) {
this.name = name;
this.isDone = false;
this.limit = limit;
}
LimitTask.prototype.alertName = function() { alert(this.name); };
LimitTask.prototype.alertLimit = function() { alert(this.limit); };
上記は下記の様に置き換えることができます
function LimitTask(name, limit) {
Task.call(this, name);
this.limit = limit;
}
LimitTask.prototype = Object.create(Task.prototype);
LimitTask.prototype.alertName = function() { alert(this.name); };
call() はオブジェクトに所属する関数やメソッドを、別なオブジェクトに割り当てて呼び出すことができます。第一引数は、呼び出されたときに this として使用される値です。
LimitTaskのthis
がTaskのthis
としても使われるので、Taskのほうでthis.name
とやったら、this
が指しているオブジェクトは同じなので、LimitTaskにname
プロパティができる
あとは、LimitTask
のプロトタイプにTask
のプロトタイプオブジェクトを追加してやります。
そうすると、プロトタイプチェーンのおかげで使用できます。
const limitTask = new LimitTask('やばいタスク', 'あした');
limitTask.alertName();
limitTask.alertLimit();
#クラス
ES6で登場した、上記で今まで記したもののシンタックスシュガー(糖衣構文)としてclass
を用いることができます。
糖衣構文: 複雑でわかりにくい書き方と全く同じ意味になるものを、よりシンプルでわかりやすい書き方で書くことができるもの
classを使い、同様にコンストラクタ関数を定義してみます。
class Task
constructor (name) {
this.name = name;
this.isDone = false;
}
}
メソッドを実装してみます
class Task
constructor (name) {
this.name = name;
this.isDone = false;
}
alertName() {
alert(this.name);
}
}
継承してみます
class LimitTask extends Task
constructor (name, limit) {
super(name);
this.limit = limit;
}
alertLimit() {
alert(this.limit);
}
}
super();
で基底のオブジェクトのコンストラクタを呼び出しています。
他にもgetter setterや静的メソッドもあります。
#おわりに
とりあえず、アウトプットに簡易Todoリストを作ってみた。
いまいち感がすごいのでよりよい方法有りましたらご助言いただけますと幸いです。
See the Pen OOP ToDo by natusme (@natsume0718) on CodePen.