Upload
ryuichi-tanaka
View
1.445
Download
1
Embed Size (px)
DESCRIPTION
プロトタイプベースオブジェクト指向プログラミング(親子関係を維持した継承)
Citation preview
Web技術勉強会2011/05/14
Ryuichi TANAKA/@mapserver2007/summer-lights.jp
JavaScriptでプロトタイプベースオブジェクト指向プログラミング
~親子関係を維持してクラスを使わないオブジェクト指向プログラミング手法~
Introduction
� この違い、わかりますか?
①var Phone = {
type: function() {
return “basic phone”;
}
};};
②var Phone = function() {};
Phone.prototype = {
type: function() {
return “basic phone”;
}
};
Introduction
� 呼び出し方が違う
� ①:Phone.type();
� ②:var phone = new Phone(); phone.type();
� メモリ効率が違う
� ①:オブジェクトとして定義したときにメモリを確保
� ②:インスタンスを生成したときにメモリを確保
� 形式が違う
� ①:オブジェクト
� ②:関数(クラス)
どちらを使うべきか
� オブジェクト指向プログラミングするなら②
� クラスを定義する
� var Phone = funciton() {};
� メソッドを定義する
� Phone.prototype = { … }:
� Phone.prototype.type = function() { … };� Phone.prototype.type = function() { … };
� 継承する
� Phone.type = new Telephone(); // 簡易な継承方法
� インスタンス化
� var obj = new Phone();
� 他言語のOOPと比較的似ている
� いろいろ試行錯誤した結果、これがベスト
と、思ってた時期が先週まであったと、思ってた時期が先週まであった
さっきのやり方はベストではないと思い直した
� 見直したきっかけ� http://blog.tojiru.net/article/199670885.html
� やっぱりクラスではなくオブジェクトとして扱うのがベストなのではないか� JavaScriptはプロトタイプベースのオブジェクト指向プログ
ラミング言語� さっきのはクラスベース�
� 非効率だった� わざわざオブジェクトをクラス(にみたてて)化、使うときに
newで再度オブジェクト化している
� どうすればよいか� いずれにしてもクラス化する方法はベストではない
� GoodPartsにもnewはBadPartsであると明記されている� JavaScriptパターンではモダンではない旨が明記されている
� オブジェクトのまま扱う方法がベストではないのか
こうすればよい(のではないか)
� 実装目標
� オブジェクトのまま扱う(クラス化しない)
� new禁止
� 感覚としてはモジュールのような感じ
� オブジェクトのまま継承する
� 親子関係を維持する� 親子関係を維持する
� http://blog.tojiru.net/article/199670885.htmlのやり方では不十分。親子関係が消滅している。
� Rubyっぽくしたい
� 関数で継承しないでメソッドで継承
件のサイトについて
� http://blog.tojiru.net/article/199670885.html
� 基本的にかなり参考になる(ブコメもすごい数)
� が、問題がある。かなり重大な。
� 「継承」ではない
� 継承と言いつつ、プロパティを上書きしているため親子�
関係は消滅。つまり、同じメソッド名は定義できない。
� 個人的な美的感覚にマッチしていない
� 継承方法がちょっとカッコ悪い(関数で継承)
� これは別に悪くない。
� 継承順が逆
� これはだめ
件のサイトについて� 継承順について
� サイトではこうなっている
� これは、extend(Animal, Dog); という意味。
var Dog = object(Animal, {
name: “犬”,
bowwow: function() { alert(“わんわん!”); }});
� これは、extend(Animal, Dog); という意味。� 英文にすると、Animal extend Dog
� 動物は犬を継承→???
� つまり、逆でないとおかしい� Dog extend Animal� extend(Dog, Animal);
� これらの点を解消してあげればかなりよい方法が導けそう
「JavaScriptらしさ」を損なわないでオブジェクト指向化
� 美的感覚にマッチした実装� Dog.extend(Animal)みたいな継承方法
� 親子関係を維持する� プロトタイプチェーンを使う
� 親のメソッドを参照できるようにする� var obj = Dog.extend(Animal);� var obj = Dog.extend(Animal);
� obj.parent.getName();
� 複数の継承を許可� 単一継承(ただし連鎖可能)
� var dog = Retriever.extend(Dog).extend(Animal);
� 多重継承(ただし後勝ち)� var dog = Retriever.extend(Dog, Animal);
プロトタイプチェーンとは
� オブジェクトに存在するプロパティのひとつ
� 参照しているオブジェクトがあればそのオブジェクトを指す
� 例:Array->Object
� Object.prototype === nullのときチェーンは終了
http://igeta.cocolog-nifty.com/blog/2007/04/prototype.html
prototypeと__proto__
� __proto__プロパティはプロトタイプチェーンをたどるためのプロパティ
� prototypeプロパティは関数オブジェクトを拡張するためのプロパティ
� __proto__はアクセスされたメンバ変数が存在しないとき、代わりにどのオブジェクトを参照するかという参照先を指す
� __proto__はIEで未実装
継承を実装するには
� __proto__を動的にたどって、__proto__の指すポインタを動的に組み替えればいい
動的に__proto__をたどる
� これはできない
� child = child.__proto__;
� child.__proto__ = parent; // Cyclic error
� これはできる
� child.__proto__.__proto__ = parent;
� だがこれは静的にたどってるのでNG
� 解決策
� var list = [“child”, “__proto__”, “__proto__”];
� eval(list.join(“.”) + “ = parent”);
親を参照するには
� 自分自身の__proto__を参照するだけ
完成形
� https://gist.github.com/967863
問題:parentが関数になってしまう
� obj.parent().getName() になってしまう
� obj.parent.getName() が理想だったが…
� 解決策はない
� 理由
� parent()では自分自身(this)を参照し__proto__を参照する
� 自分自身(this)を参照するには関数でなければならない
� parentだとただのプロパティであり、thisはwindowになる
� 悪あがき(無名関数を駆使したり)したがやっぱり無理でした。
� 正直、こんなことにこだわる理由はないのだが…。
� 大した問題じゃないので次。
問題:prototype汚染問題� prototypeを拡張することで全コードに影響が出る問題
� Object.prototype.extend� Array.prototype.each など� ライブラリ拡張してたりする。ライブラリ間で競合していると動かなく
なる可能性がある� for-inでプロパティを走査したときに拡張したメソッドが含まれるため
バグを生む可能性がある
� 回避方法� メソッド化を諦めて素直に関数を使う� ラッパーオブジェクトでラップする
� http://d.hatena.ne.jp/cyokodog/20081031/ArrayExtend01
� ライブラリを使う� jQueryとかを素直に使っておけということ
� そのライブラリが汚染していることがあるけどね
� 今回の場合� mix, parentを追加したので影響はある� が、かぶることは(おそらく)ないので目をつぶる。for-inは気をつける。
今回の方法を応用すると
� 機能ごとの汎用オブジェクトを定義しておく� var Utils = {…};
� var Http = {…};
� var Design = {…};
� 各アプリごとで使いたいオブジェクトだけ継承する� var obj = Base.mix(Utils).mix(Http);
� 名前がかぶらなければそのまま呼べる� obj.xhr(); // Http#xhr
� 名前がかぶってもparent()で呼べる� obj.parent().getObjectName(); // Utils#getObjectName
� 汎用オブジェクトの単独使用も可能� var result = Http.xhr();
� 他のライブラリと併用はもちろん可能
まとめ
� JavaScriptのプロトタイプベースオブジェクト指向プログラミングをするための方法を説明
� 過去のプログラムをこの方法で書きなおすかも