LoginSignup
4
2

More than 5 years have passed since last update.

JavaScriptのプロトタイプ

Last updated at Posted at 2017-03-26

元ねた

JavaScriptのプロトタイプからオブジェクト指向を学ぶ
・prototypeを使った例

moto1.js
function Dog(cry) {
    this.cry = cry;
}

Dog.prototype.bark = function() {
    console.log(this.cry);
}

var dog = new Dog('bow');
dog.bark();

こう書き換えると・・

・プロトタイプっぽい何か

ex1.js
function Cat(cry) {
    this.cry = cry;
}

var proto = {}
proto.bark = function() {
    console.log(this.cry);
}

var cat = new Cat('mew');
cat.bark = proto.bark;
cat.bark(); // 'mew'

var cat2 = new Cat('mew mew');
cat2.bark = proto.bark;
cat2.bark(); // 'mew mew'

prototypeとただのオブジェクト(コード例のproto)は、どこが違うの?

・prototypeの場合は、なければ親を探しに行ってくれる
(親を設定した場合に限る。コード例(ex2)の、ExDog.prototype = Object.create(Dog.prototype);の部分。)
・prototypeを使うと、コードが数行減る
(コード例(ex1)の、var proto = {}cat.bark = proto.bark;の部分)

ex2.js
function ExDog(cry)
{
    this.cry = cry;
}

ExDog.prototype = Object.create(Dog.prototype);

var exDog = new ExDog('bow wow');
exDog.bark();

この例では、ExDog.barkは存在しないので、コード例(moto1)のDog.barkを探しに行く。
そこにも無ければ、Object.barkを探し・・その先はチェーンの末端なので見つからないと怒られる。

ポイント

・(自動的な)プロパティの継承を考えるなら、prototypeを使う。
ただし、プロトタイプチェーンが長くなると、見つからない場合は深い所まで探しに行かないといけないので、パフォーマンスの低下につながる場合がある。

参考資料

MDN - 継承とプロトタイプチェーン
や...やっと理解できた!JavaScriptのプロトタイプチェーン
MDN - Function プロトタイプオブジェクト

目を通しておいた方がよいページ
MDN - Object.prototype.__proto__

蛇足

・プロトタイプもどきで、継承と似たようなことをやろうとすると・・

da1.js
function ExCat(cry)
{
    this.cry = cry;
}

var exProto = Object.create(proto);

var exCat = new ExCat('みゅみゅ?');
exCat.bark = exProto.bark;
exCat.bark(); // 'みゅみゅ?'

面倒!(というより、色々書くのを忘れそう)
(実際は、もう少し楽な書き方ができますが・・)

蛇足2:ありがちな失敗

書く順番を間違える

~~is not foundや、~~is not a functionなどのエラーが表示されるパターン。
(エラーが表示される場合は)使われる側を、使う側より前に書きましょう。(他の言語で言う所の、プロトタイプ宣言みたいな感じで)

参考:
MDN - 関数宣言の巻き上げ

失敗例 (da2-1.js)

cat.f1()は、Cat.prototype.f1より後に置く必要がある。
f1()f2()を呼ぶので、f2()f1()の順に並べる必要がある。

da2-1.js
var cat = new Cat('wild cat'); // Ok
cat.f1(); // TypeError: cat.f1 is not a function (prototype.f1より前にあるのでエラー)

function Cat(cry) {
    this.cry = cry;
}

Cat.prototype.f1 = function() {
    console.log('f1');
    this.f2(); // TypeError: this.f2 is not a function
}

cat.f1(); // f1()は呼べる。ただし、`this.f2()`がエラーを表示する。

Cat.prototype.f2 = function() {
    console.log('f2');
}

失敗例 (da2-2.js)

Cat(cry)の中で、Bird.prototype.chain1が必要になるので、new Cat('wild cat')Bird.prototype.chain1より後に置く必要がある。

da2-2.js
function Bird(cry) {
    this.cry = cry;
}

var cat = new Cat('wild cat'); // TypeError: (intermediate value).chain1 is not a function

Bird.prototype.chain1 = function() {
    console.log('chain1');
    return this;
}

var cat2 = new Cat('wild cat2'); // ここなら大丈夫。

function Cat(cry) {
    this.cry = cry;
    this.bird = new Bird('I can fly!')
    .chain1();
}

チェーンの罠にはまる

~~is undefinedなどのエラーが表示されるパターン。
(this自体が別物か、プロパティが作られる前にアクセスした場合などに発生します)

※これは、プロトタイプの罠というよりも、チェーンを使う場合のポイントです。
(でも、プロトタイプだからthisの扱いで間違えた?と考えやすい所。)

JavaScriptでは、オブジェクトの中身(プロパティ)を後から増やせるので、いつ増えたのかを気にしながら使いましょう。
MDN - オブジェクトとそのプロパティ
MDN - object の型に対してプロパティを定義するなどを参照。
※参照先の前者はthis、後者はprototypeについての説明なので注意しましょう。

※thisが指す先がライブラリだった場合は・・怖いことに。(ライブラリ内部で使用している名前と重複した場合など)

失敗例 (da2-3.js) thisの中身に注意しよう!

da2-3.js
// da2-2を少し改造。

function Bird(cry) {
    this.cry = cry;
}

Bird.prototype.chain1 = function() {
    console.log('chain1');
    return this;
}

Bird.prototype.chain2 = function(self) {
    var bird = self.bird; // birdはundefinedになる
    console.log(bird.cry); // TypeError: bird is undefined
    return this;
}

var cat = new Cat('wild cat');

function Cat(cry) {
    this.cry = cry;
    this.bird = new Bird('I can fly!')
    .chain1()
    .chain2(this); // この段階では、this.birdはセットされていない
}

こうすれば、期待通りの動作に。(da2-3-ok.js)
・チェーンを切り離す

da2-3-ok.js
function Cat(cry) {
    this.cry = cry;
    this.bird = new Bird('I can fly!'); // this.birdがセットされる
    this.bird.chain1()
    .chain2(this);
}
4
2
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
4
2