JavaScriptのプロトタイプ

#元ねた
[JavaScriptのプロトタイプからオブジェクト指向を学ぶ](http://qiita.com/takeharu/items/809114f943208aaf55b3)
・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 - 継承とプロトタイプチェーン](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain)
[や...やっと理解できた！JavaScriptのプロトタイプチェーン](http://maeharin.hatenablog.com/entry/20130215/javascript_prototype_chain)
[MDN - Function プロトタイプオブジェクト](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function#Function_%E3%83%97%E3%83%AD%E3%83%88%E3%82%BF%E3%82%A4%E3%83%97%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88)

目を通しておいた方がよいページ
[MDN - Object.prototype.\_\_proto\_\_](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/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 - 関数宣言の巻き上げ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/function#Function_declaration_hoisting)

###失敗例 (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 - オブジェクトとそのプロパティ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Objects_and_properties)、
[MDN - object の型に対してプロパティを定義する]((https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_properties_for_an_object_type))などを参照。
※参照先の前者は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);
}
```