20
Web技術勉強 2011/05/14 Ryuichi TANAKA/@mapserver2007/summer-lights.jp JavaScriptでタイスオジェク指向ググ ~親子関係を維持してクスを使わないオジェク指向ググ手法~

Web技術勉強会 20110514

Embed Size (px)

DESCRIPTION

プロトタイプベースオブジェクト指向プログラミング(親子関係を維持した継承)

Citation preview

Page 1: Web技術勉強会 20110514

Web技術勉強会2011/05/14

Ryuichi TANAKA/@mapserver2007/summer-lights.jp

JavaScriptでプロトタイプベースオブジェクト指向プログラミング

~親子関係を維持してクラスを使わないオブジェクト指向プログラミング手法~

Page 2: Web技術勉強会 20110514

Introduction

� この違い、わかりますか?

①var Phone = {

type: function() {

return “basic phone”;

}

};};

②var Phone = function() {};

Phone.prototype = {

type: function() {

return “basic phone”;

}

};

Page 3: Web技術勉強会 20110514

Introduction

� 呼び出し方が違う

� ①:Phone.type();

� ②:var phone = new Phone(); phone.type();

� メモリ効率が違う

� ①:オブジェクトとして定義したときにメモリを確保

� ②:インスタンスを生成したときにメモリを確保

� 形式が違う

� ①:オブジェクト

� ②:関数(クラス)

Page 4: Web技術勉強会 20110514

どちらを使うべきか

� オブジェクト指向プログラミングするなら②

� クラスを定義する

� var Phone = funciton() {};

� メソッドを定義する

� Phone.prototype = { … }:

� Phone.prototype.type = function() { … };� Phone.prototype.type = function() { … };

� 継承する

� Phone.type = new Telephone(); // 簡易な継承方法

� インスタンス化

� var obj = new Phone();

� 他言語のOOPと比較的似ている

� いろいろ試行錯誤した結果、これがベスト

Page 5: Web技術勉強会 20110514

と、思ってた時期が先週まであったと、思ってた時期が先週まであった

Page 6: Web技術勉強会 20110514

さっきのやり方はベストではないと思い直した

� 見直したきっかけ� http://blog.tojiru.net/article/199670885.html

� やっぱりクラスではなくオブジェクトとして扱うのがベストなのではないか� JavaScriptはプロトタイプベースのオブジェクト指向プログ

ラミング言語� さっきのはクラスベース�

� 非効率だった� わざわざオブジェクトをクラス(にみたてて)化、使うときに

newで再度オブジェクト化している

� どうすればよいか� いずれにしてもクラス化する方法はベストではない

� GoodPartsにもnewはBadPartsであると明記されている� JavaScriptパターンではモダンではない旨が明記されている

� オブジェクトのまま扱う方法がベストではないのか

Page 7: Web技術勉強会 20110514

こうすればよい(のではないか)

� 実装目標

� オブジェクトのまま扱う(クラス化しない)

� new禁止

� 感覚としてはモジュールのような感じ

� オブジェクトのまま継承する

� 親子関係を維持する� 親子関係を維持する

� http://blog.tojiru.net/article/199670885.htmlのやり方では不十分。親子関係が消滅している。

� Rubyっぽくしたい

� 関数で継承しないでメソッドで継承

Page 8: Web技術勉強会 20110514

件のサイトについて

� http://blog.tojiru.net/article/199670885.html

� 基本的にかなり参考になる(ブコメもすごい数)

� が、問題がある。かなり重大な。

� 「継承」ではない

� 継承と言いつつ、プロパティを上書きしているため親子�

関係は消滅。つまり、同じメソッド名は定義できない。

� 個人的な美的感覚にマッチしていない

� 継承方法がちょっとカッコ悪い(関数で継承)

� これは別に悪くない。

� 継承順が逆

� これはだめ

Page 9: Web技術勉強会 20110514

件のサイトについて� 継承順について

� サイトではこうなっている

� これは、extend(Animal, Dog); という意味。

var Dog = object(Animal, {

name: “犬”,

bowwow: function() { alert(“わんわん!”); }});

� これは、extend(Animal, Dog); という意味。� 英文にすると、Animal extend Dog

� 動物は犬を継承→???

� つまり、逆でないとおかしい� Dog extend Animal� extend(Dog, Animal);

� これらの点を解消してあげればかなりよい方法が導けそう

Page 10: Web技術勉強会 20110514

「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);

Page 11: Web技術勉強会 20110514

プロトタイプチェーンとは

� オブジェクトに存在するプロパティのひとつ

� 参照しているオブジェクトがあればそのオブジェクトを指す

� 例:Array->Object

� Object.prototype === nullのときチェーンは終了

http://igeta.cocolog-nifty.com/blog/2007/04/prototype.html

Page 12: Web技術勉強会 20110514

prototypeと__proto__

� __proto__プロパティはプロトタイプチェーンをたどるためのプロパティ

� prototypeプロパティは関数オブジェクトを拡張するためのプロパティ

� __proto__はアクセスされたメンバ変数が存在しないとき、代わりにどのオブジェクトを参照するかという参照先を指す

� __proto__はIEで未実装

Page 13: Web技術勉強会 20110514

継承を実装するには

� __proto__を動的にたどって、__proto__の指すポインタを動的に組み替えればいい

Page 14: Web技術勉強会 20110514

動的に__proto__をたどる

� これはできない

� child = child.__proto__;

� child.__proto__ = parent; // Cyclic error

� これはできる

� child.__proto__.__proto__ = parent;

� だがこれは静的にたどってるのでNG

� 解決策

� var list = [“child”, “__proto__”, “__proto__”];

� eval(list.join(“.”) + “ = parent”);

Page 15: Web技術勉強会 20110514

親を参照するには

� 自分自身の__proto__を参照するだけ

Page 16: Web技術勉強会 20110514

完成形

� https://gist.github.com/967863

Page 17: Web技術勉強会 20110514

問題:parentが関数になってしまう

� obj.parent().getName() になってしまう

� obj.parent.getName() が理想だったが…

� 解決策はない

� 理由

� parent()では自分自身(this)を参照し__proto__を参照する

� 自分自身(this)を参照するには関数でなければならない

� parentだとただのプロパティであり、thisはwindowになる

� 悪あがき(無名関数を駆使したり)したがやっぱり無理でした。

� 正直、こんなことにこだわる理由はないのだが…。

� 大した問題じゃないので次。

Page 18: Web技術勉強会 20110514

問題:prototype汚染問題� prototypeを拡張することで全コードに影響が出る問題

� Object.prototype.extend� Array.prototype.each など� ライブラリ拡張してたりする。ライブラリ間で競合していると動かなく

なる可能性がある� for-inでプロパティを走査したときに拡張したメソッドが含まれるため

バグを生む可能性がある

� 回避方法� メソッド化を諦めて素直に関数を使う� ラッパーオブジェクトでラップする

� http://d.hatena.ne.jp/cyokodog/20081031/ArrayExtend01

� ライブラリを使う� jQueryとかを素直に使っておけということ

� そのライブラリが汚染していることがあるけどね

� 今回の場合� mix, parentを追加したので影響はある� が、かぶることは(おそらく)ないので目をつぶる。for-inは気をつける。

Page 19: Web技術勉強会 20110514

今回の方法を応用すると

� 機能ごとの汎用オブジェクトを定義しておく� 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();

� 他のライブラリと併用はもちろん可能

Page 20: Web技術勉強会 20110514

まとめ

� JavaScriptのプロトタイプベースオブジェクト指向プログラミングをするための方法を説明

� 過去のプログラムをこの方法で書きなおすかも