JavaScriptのオブジェクト指向は、逆の順番で学んだほうが理解しやすいと思うので…

※この投稿は 2011/03/10 に [こちら](https://yanother.blogspot.com/2011/03/javascript.html) に投稿した記事の転載です。


# これを書いた経緯

　事の発端というか、きっかけは、[id:perlcodesample](http://d.hatena.ne.jp/perlcodesample/)さんと[id:gfx](http://d.hatena.ne.jp/gfx/)さんの下のポストを見て、

- [JavaScriptで一番簡単にオブジェクト指向プログラミングを行う方法](http://d.hatena.ne.jp/perlcodesample/20101124/1278596435) ([id:perlcodesample](http://d.hatena.ne.jp/perlcodesample/)さん)
- [JavaScriptにおけるオブジェクトの定義](http://d.hatena.ne.jp/gfx/20110226/1298705892) ([id:gfx](http://d.hatena.ne.jp/gfx/)さん)

　`new` とか `prototype` を使うのが推奨されてないとか、直接代入するほうが楽とかじゃなくて、挙動が違うんだよなぁ、と思ったこと。
　挙動が違うんだから、もちろん使いどころも違うんですよね。

　でも実際、JavaScriptのオブジェクト指向は混乱しやすいと思います。
　自分もご多分にもれず、さんざん混乱させられたクチですしね。
　わかってしまえば、どってことなくて、とってもシンプルなんですけどね。

　せっかくなので、今だからこそ言える、自分だったらこうやって教えて欲しかったなぁ、っていう説明をしてみようかと思います。
　題して、JavaScriptのオブジェクト指向は、**逆から入門しろ！**


# 逆からってどこから？ → Object.createから

　思うに、JavaScriptのオブジェクト指向は、`new` から入るよりも、`Object.create` から入ったほうが理解しやすいと思うんですよね。
　`Object.create` っていうのは、新しいJavaScriptの仕様（ECMAScript 5th Edition）で標準に取り入れられたメソッドで、引数で指定したオブジェクトをプロトタイプとする新しいオブジェクトを作ることができます。
　こんなふうにです:

```javascript
var mam = {
  given_name : "サザエ",
  family_name: "フグタ",
  who_am_i   : function(){ alert(this.family_name + this.given_name);  }
};

var kid = Object.create(mam);
kid.given_name = "タラオ";

mam.who_am_i();  // フグタサザエ
kid.who_am_i();  // フグタタラオ
```

　kidがmamのメソッドを継承していることがわかりますね。

　そもそもJavaScriptのオブジェクト指向ってプロトタイプベースの継承なわけで、`new` とかコンストラクタっていうのはなくてもいいんです。
　この `Object.create` メソッドからはじめて、従来からある `new` とコンストラクタ、それから`prototype` プロパティの挙動をみていこうと思います。
　つまり、歴史的に逆順というわけです。

　ちなみに、この `Object.create` メソッドは、モダンなブラウザなら既に実装されているので、実際に実行して試すことができます。（今回自分は、Chrome 9.0で動作確認をしました。）


# そもそもプロトタイプってなんなん？

　上のコードを見て、「`Object.create` の中では新しいオブジェクトを生成して `mam` のプロパティをコピってるんだな、こんなふうに↓」

```javascript
Object.create = function ( o ) {
    var p = {};
    for ( var i in o ) {
        p[i] = o[i];
    }
    return p;
};
```

　と思ったアナタ！イイ線いってます！
　でも、事はそれほど単純ではありません。次のコードを続けて実行してみましょう:

```javascript
mam.call = function(){ alert("カツオ！"); };

mam.call();  // カツオ！
kid.call();  // カツオ！
```

　`call` は `mam` にしか追加していないのに、`kid` のほうでも使えるようになってしまいましたよ。
　実は `mam` と `kid` は同じオブジェクトを指している？いやいや、上ので `who_am_i` を呼んだ時には、ちゃんと別々になってましたよね。

　種明かしをすると、`kid` は自身にセットされた `given_name` プロパティ以外はなんにも持っていない、ほとんど空っぽのオブジェクトです。
　ただし、自分が持っていないプロパティについては `mam` に問い合わせて、`mam` のものを自分のものとして使おうとするようになっています。
　実は `kid` はユーザの気づかないところで `mam` への参照を隠し持っていて、自分が知らないことは`mam` のマネをするわけなんですね。
　このような特別な関係があるとき、`mam` は `kid` のプロトタイプだと言います。

　また、`kid` が `mam` に問い合わせたプロパティを `mam` も持っていなかったとしましょう。
　すると `mam` は、やはり自分が持っている隠し参照を使って、自分自身のプロトタイプへと問い合わせを伝搬させます。

　このようにプロパティの探索をたどっていくリンクの連なりを、プロトタイプチェインと呼んだりします。
　最終的に、この鎖の終端は通常は `Object.prototype` になっており、終端にたどりついてもまだ該当するプロパティが見つからなかった場合に、プロパティが未定義ということになります。

　`Object.create` は、オブジェクト間でこのような特別な関係を築いてくれるわけですね。


# オーバーライドは子供で値を設定するだけ

　ところで、上の `call` メソッドでは、`kid` も `mam` のマネをしてカツオを呼び捨てにしてしまっていました。
　ここはひとつ、もう少し子供らしく呼ぶようにしつけをしてあげましょう:

```javascript
kid.call = function(){ alert("カツオおにいちゃん"); };
kid.call();  // カツオおにいちゃん
```

　プロパティ探索の挙動がわかってしまえば、挙動を変えたい場合は単に値を代入してしまえば良いことがわかりますね。
　もちろん、この段階でも `mam` の `call` の値は元のままです。

```javascript
mam.call();  // カツオ！
```

　逆に、`kid` 独自の挙動を元に戻したいと思ったら、プロパティを削除してやればOKです:

```javascript
delete kid.call;
kid.call();  // カツオ！
```

　`kid` からプロパティを消しても、`mam` からも消えるわけではないので、ふたたびマネをするようになるわけですね。


# 継承とMix-in

　ただ同じ振る舞いをさせたいだけであれば、単純に片方のオブジェクトからもう片方のオブジェクトへとすべてのプロパティをコピーしてしまえば良い話です。
　このように既存の実装コードを、まったく関係のないオブジェクト間で再利用するというのは、継承ではなくMix-in（Rubyのincludeとか）の考え方です。
　利用ケースによっては、Mix-inと継承はまったく同じような結果をもたらします。

　しかしプロトタイプ継承を使うと、親への機能拡張（メソッドの追加）が、すぐさま子供からも使えるようになるという点が大きな違いです。
　実行時に、プロトタイプチェインに連なっているすべてのオブジェクトについて、一度の変更で機能を追加・変更できます。
　また、どれだけたくさんの子オブジェクトが作られていても、たとえ子オブジェクトにアクセスすることができなかったとしても、変更の影響を及ぼすことができます。
　Mix-inとは違って、親子オブジェクト間で特別な絆ができている継承でなくては、こう柔軟にはいきませんよね。

　しかし、継承がかさんでプロトタイプチェインがあまりに長くなってしまうと、ずっと親の代で定義されているプロパティを探索するのに多くの時間がかかってしまうことにもなります。（このあたりは処理系の最適化にも大きく依存しますが）
　だからといって、なんでもかんでもそれぞれのオブジェクトが自分で持っていては、メモリ効率も悪くなるでしょうし、継承を使った柔軟な挙動変更もできなくなってしまいます。

　結局、継承とMix-inは適材適所。
　その場で相応しいほうを使い分けることができてこそ、真のJavaScripterというものですよね！


# プロトタイプ関係を作れるのはnewだけ（だった）

　ここからは、上で使った `Object.create` の機能を、従来のJavaScriptだけで実装してみましょう。

　いきなりコードで書くと、こういうことになります:

```javascript
Object.create = function ( o ) {
    function dummy(){}
    dummy.prototype = o;
    return new dummy();
}
```

**（※実際の `Object.create` は、変更不可能なプロパティを定義できたりと、3rd Editionの範囲では決して実現できなかった機能も含む高機能なものです！ここではあくまで、プロトタイプの継承関係の構築のみに焦点を当てています。）**

　空の関数である `dummy` を `new` して返す前に、`dummy` の `prototype` プロパティにプロトタイプとなるオブジェクトを設定しているのがミソです。
　JavaScriptでは、 `new F(ARGS)` という式は、次のように処理されます:

1. 新しい空のオブジェクトを作る
2. 新しいオブジェクトのプロトタイプとして `F.prototype` を設定する
3. `F(ARGS)` を呼び出す。この時、呼び出され側の `this` の値として新しく作ったオブジェクトを使う
4. 3の結果がオブジェクトだったら、これを結果として返す。
5. そうでなければ、1で作ったオブジェクトを返す

　擬似コード的に書くと、こんなかんじ:

```javascript
function new ( F, ARGS ) {
    var o = {};
    o.__proto__ = F.prototype;
    var r = F.apply(o, ARGS);
    return r != null && r instanceof Object ? r : o;
}
```

　あれ、ここで使ってる `__proto__` って、どこかで見たことありませんか？
　そうです。継承するコードでたまに出てくるやつですね。FirefoxとかChromeとか、特定の処理系だけで使えます。
　実はこれが、上で言っていた「ユーザが気づかないところで持っている隠し参照」のことです。
　処理系によっては、これがユーザから直接触れるようになっているということですね。

　直接いじれちゃえば、もっと細かい制御もやり放題なので、これは便利！なのですが、もちろん、ブラウザ間の互換性が損なわれるので、あまりオススメはしません。
　非標準なAPIは、ちゃんと使える場所と使いどころとをわきまえて使いましょうね。JavaScripterとの約束だよ！

# newいらない子(ェ

　上で `new` を実装するために `__proto__` プロパティを使いましたが、 `Object.create` を標準で手に入れた我々は今や、`__proto__` に頼らずともオブジェクトの親子関係を作れる力を手にしたのでした。
　つまり、`new` に相当する機能をJavaScript標準の機能だけで実装することができるようになったのです！
　こんなふうに：

```javascript
function New ( F, ARGS ) {
    var o = Object.create(F.prototype);
    var r = F.apply(o, ARGS);
    return r != null && r instanceof Object ? r : o;
}

function Cat ( name ) {
    this.name = name;
}
Cat.prototype.caterwaul = function(){
    alert(this.name + "「ニャーニャー」");
};

var cat = New(Cat, ["タマ"]);
cat.caterwaul();  // タマ「ニャーニャー」
```

　上では `new` を使って `Object.create` を実装する例を示しましたが、今回はその逆です。
　つまり、`Object.create` が最初からあれば、`new` っていらなかったってことですね(>_<)


# 蛇足： なんでわざわざ関数のプロパティを経由するようになってるの？

　なんででしょうね？
　ここからは勝手に推測してみますが、たぶん、関数だけでなんとかしてJavaのコンストラクタの見た目をマネしたかったんじゃないでしょうか？

　普通は、一連の機能をもったオブジェクトをただひとつ一点物で作る（シングルトンパターンみたいな）ケースよりも、同じ機能をもったオブジェクトをたくさん量産するケースのほうが多いと思います。
　すると当然、オブジェクトの初期化を行う処理をサブルーチン化しておいて、その中でオブジェクトを生成して返すことを考えますよね。
　こんなふうに:

```javascript
function bear ( name ) {
    var k = Object.create(mam);
    k.name = name;
    return k;
}

var kid = bear("タラオ");
```

　つまるところこれって、コンストラクタですよね？

　また一方で、JavaScriptはその出自から、Javaに見た目を似せなければならないという宿命を持って生まれてきました。（このあたりは、[id:badatmathさんのプレゼン資料](http://d.hatena.ne.jp/badatmath/20110308/1299554852) がとってもわかりやすいです）
　で、Javaってコンストラクタを呼び出すときには `new` をつけますよね？
　そこで、`new` をつけて関数を呼び出したときに特別な処理を持たせ、関数にコンストラクタとクラスの２つの役割を押し付けてしまおうと考えたのではないかと思います。

　そうなると、Javaのコンストラクタっぽく見せるためには、関数が `new` をつけて呼び出されたときには、新しいオブジェクトが適切に（プロトタイプから継承されて）作られ、`this` にバインドされていなければなりません。
　そこで、そのコンストラクタ（関数）がオブジェクトを生成する際に暗黙的に使うプロトタイプを、コンストラクタ自身に持たせておこう。でも、どの関数がコンストラクタとして呼び出されるかはわからないから、すべての関数にプロパティで持たせておこう。
　っていうことになったんじゃないかと。

　えっ、JavaScriptは最初からプロトタイプベースのオブジェクト指向を採用するつもりだったのかって？
　それはわかりません。Sun Microsystemsがプロトタイプベースの始祖であるSelfの研究者を抱えていたってこと以外、私は知りません。
　開発者のEich氏は、最初からSchemeのような関数型言語を作るつもりだったようですけどね。

# 書いたあとに一言

　うん！
　長々と書いたわりには、全然わかりやすくなってない気がするね(´・ω・｀)