LoginSignup
1
1

More than 3 years have passed since last update.

[メモ] JavaScript プロトタイプとクラスを学んでTodoリストを作った

Posted at

ミス等有りましたらご助言いただけますと幸いです。

コンストラクタ

オブジェクトを生成するには、 {}のようなオブジェクト初期化子を使用するか、コンストラクタ関数を用いる。
コンストラクタ関数はオブジェクトの雛形(種類)を定義する。
定義したら、new を用いてそのオブジェクトのインスタンスを作成する。

app
function Task(name) {
  this.name = name;
  this.isDone = false;
}
const task = new Task('a');

参考

プロトタイプ

オブジェクトはprototypeオブジェクトという特別なプロパティを持っている。
コンストラクタ関数と併せて使用し、インスタンス化した際に__proto__prototypeの参照がコピーされる。

app.js
// 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を使って既存のコンストラクタにメソッドを追加でき、インスタンス化したオブジェクトから呼び出すことが可能。

下記のように、メソッドを定義しないのはメモリの節約(インスタンス化するたびに定義される)のため。

app.js
function Task(name) {
  this.name = name;
  this.isDone = false;
  this.alertName = function() { ~~ };
}

プロトタイプチェーン

どのオブジェクトもプロトタイプと呼ばれる、他のオブジェクトへの内部的な繋がりを持っている。
そのプロトタイプオブジェクトもプロトタイプを持っており、あるオブジェクトのプロトタイプが null に到達するまでそれが続く。
つまり、プロトタイプが多階層になっていること。

スクリーンショット 2020-11-01 22.44.43.png

例えば、下記を実行する。

app.js
const task = new Task('a');
task.hasOwnProperty('name');

hasOwnPropertyというメソッドは、Taskオブジェクトは持っていないし、プロトタイプに定義もしていないので、
更に下のprototype オブジェクト (Objectのprototype) のhasOwnPropertyを実行する。

スクリーンショット 2020-11-01 23.12.50.png

優先順位は、コンストラクタ内に定義したものが一番最初、次にプロトタイプ、次にプロトタイプのプロトタイプ...となる

  1. プロパティの値がローカルに存在するかを確かめ、存在している場合は、その値を返します。
  2. 値がローカルに存在していない場合は、プロトタイプチェーンを確認します(proto プロパティを使用)。
  3. プロトタイプチェーン内のオブジェクトが指定したプロパティの値を持っている場合は、その値を返します。
  4. そのようなプロパティが見つからない場合は、オブジェクトにそのプロパティは存在しません。

参考

プロトタイプを使った継承

プロトタイプチェーンを使って、プロトタイプオブジェクトを下ってプロパティを使用できることがわかったので、これを用いて継承をしていく。

タスクオブジェクトのもととなるコンストラクタ関数を下記のように定義する。

app
function Task(name) {
  this.name = name;
  this.isDone = false;
}
Task.prototype.alertName = function() { alert(this.name); };

これを継承し、期限を設定できるタスクを作成したい。ついでにそれをアラートするメソッドも
まずは、同様に作成してみる。

app
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); };

上記は下記の様に置き換えることができます

app
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のプロトタイプオブジェクトを追加してやります。
そうすると、プロトタイプチェーンのおかげで使用できます。

app.js
const limitTask = new LimitTask('やばいタスク', 'あした');
limitTask.alertName();
limitTask.alertLimit();

クラス

ES6で登場した、上記で今まで記したもののシンタックスシュガー(糖衣構文)としてclassを用いることができます。

糖衣構文: 複雑でわかりにくい書き方と全く同じ意味になるものを、よりシンプルでわかりやすい書き方で書くことができるもの

classを使い、同様にコンストラクタ関数を定義してみます。

app.js
class Task
  constructor (name) {
    this.name = name;
    this.isDone = false;
  }
}

メソッドを実装してみます

app.js
class Task
  constructor (name) {
    this.name = name;
    this.isDone = false;
  }
  alertName() {
    alert(this.name);
  }
}

継承してみます

app.js
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.

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1