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

Web技術勉強会 20110528

Embed Size (px)

DESCRIPTION

プロトタイプベースOOPライブラリの紹介

Citation preview

Page 1: Web技術勉強会 20110528

Web技術勉強会2011/05/28

Ryuichi TANAKA/@mapserver2007/summer-lights.jp

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

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

Page 2: Web技術勉強会 20110528

前回の内容

� 簡単に継承

� var obj = Base.mix(Utils).mix(Http);

� mixメソッドで簡単にモジュールをMix-in

� 親子関係を維持した継承

� obj.parent().getName();

� 子と親で同じメソッドがあった場合、従来の継承では上書きされてしまうため親メソッドにアクセス不能だったが、parent()経由でアクセス可能にした

Page 3: Web技術勉強会 20110528

今回の目的� 前回の問題点の解消

� Object拡張によるprototype汚染問題� for-inに拡張したプロパティが含まれる問題� 実装の致命的ミス(あとで発覚)

� IE対応� IEにはプロトタイプチェーンがないのでメソッドのチェーン探索

ができない問題__proto__がないので親を辿れない問題� __proto__がないので親を辿れない問題

� 前回の残課題� 多重継承機能未実装

� 新たな課題� Mix-in、多重継承したオブジェクトを再継承する機能

� テスト� 自動テストで正しく動くかどうか確認する

� 公開� 正式公開(ライセンスを付与して使えるようにする)

Page 4: Web技術勉強会 20110528

前回の問題点①

� prototype汚染問題

� 起こる実害

� オブジェクトを作ると必ず追加したメソッドmix, parentが付随してきてしまう

� これによりmix, parentが予約させるためライブラリ使用者が再定義不能、for-inループ時にmix, parentが出現す者が再定義不能、for-inループ時にmix, parentが出現する問題が発生するvar obj = {}; // = new Object();

for (var prop in obj) {

alert(prop); // mix, parent

}

Page 5: Web技術勉強会 20110528

前回の問題点①

� prototype汚染はやはり避けるべきである

� 汎用性を求めない、プロジェクトの一部でしか使わないなど、範囲がごく狭い場合ではあるいは許容してもいいかもしれないが、一般的には避けるべき。� 当初、狭い範囲のライブラリの扱いだったが、急遽汎用

性を求めるように方向転換したため変更の必要が出た。性を求めるように方向転換したため変更の必要が出た。

� for-inに含まれるのがあまりに大きい。回避も可能だが、毎回Object#hasOwnPropertyでチェックしないといけない。hasOwnPropertyの存在を知らない人が使ったら100%バグになる。� これは無視できない問題。この問題がある以上

prototype汚染は避けないと後々つらいことになる。

Page 6: Web技術勉強会 20110528

前回の問題点①

� 実は前回の内容には決定的なバグが存在していた� これはひどい。先週時点では糞ライブラリと言われても

反論できないです…。

� mixi-inで「オブジェクト自体」を拡張していた

� つまり…var obj = Iphone.mix(Feature).mix(Telephone);

� もとのオブジェクト(Iphone)が勝手に拡張されるため、Mix-in後に単独で使ったりするとおかしなことになる。

var obj = Iphone.mix(Feature).mix(Telephone);

alert(obj.parent().getPhoneName()); // garake-

var obj2 = Iphone; // 継承しないalert(obj2.parent().getPhoneName()); // garake- \(^o^)/

Page 7: Web技術勉強会 20110528

前回の問題点①を解決

� mix, parentメソッドを動的に追加するモジュールを一つ定義するように変更。

� このモジュールを経由してオブジェクトを生成するルールとする。

� Objectを拡張しないので{}.mix(), {}.parent()は実行できない=for-in問題も解決。

var Module = {};

Module.create = function() {

// …

};

Page 8: Web技術勉強会 20110528

前回の問題点①を解決

� オブジェクト自体を拡張しないようにする

� ここで言うオブジェクトとは、Objectのことではなく利用者が定義したモジュールオブジェクトのこと

� JavaScriptは参照渡しが基本

� そのままオブジェクト(this)を拡張するともとのオブジェクトにも影響が出てしまうクトにも影響が出てしまう

� JavaScriptにはディープコピーがない

� var copy = origin; とやってもシャローコピーになる

� copyを拡張するとoriginも拡張される

� var copy = clone(origin); のようにディープコピーする処理を実装してあげることで解決する

Page 9: Web技術勉強会 20110528

前回の問題点①を解決var Iphone = Module.create({

getPhoneName: function() {

return “iphone”;

}

});

// alert(Iphone.hasOwnProperty(“mix”)); // true

// alert(Iphone.hasOwnProperty(“parent”); // true// alert(Iphone.hasOwnProperty(“parent”); // true

// Feature, Telephoneも同様に定義

var obj = Iphone.mix(Feature).mix(Telephone);

alert(obj.parent().getPhoneName()); // garake-

var obj2 = Iphone;

alert(obj2.parent().getPhoneName()); // undefined method

Page 10: Web技術勉強会 20110528

前回の問題点②

� IE未対応

� 趣味なら問題ない(公開してるシステムはほとんどIE未対応)

� だが、個人的な野望として仕事で使いたいと密かに思ってるのでこれはなんとかしたい。

� 仕事ではIEだらけ。避けて通れない…。� 仕事ではIEだらけ。避けて通れない…。

� ライセンスさえクリア出来れば仕事でも使える。

� とはいえ簡単じゃない

� モダンブラウザでできた__proto__による継承関係、プロトタイプチェーンを利用した親のメソッド自動探索が使えない。つまりこれを自前で作らないといけない。

� この問題が今回の最難関

Page 11: Web技術勉強会 20110528

前回の問題点②を解決

� __proto__を使わないで動的に親子関係を構築する

� 以下のパターン全てに対応する(パターンが多いので一部省略、テストでは全部実施)

� 単独Mix-inパターン

� Iphone.mix(Feature)

� 単独Mix-in連鎖パターン

� Iphone.mix(Feature).mix(Telephone)

� 多重継承パターン

� Iphone.mix(Feature, Telephone)

� 多重継承連鎖パターン

� Iphone.mix(Fetature, Telephone).mix(Android, Ipad)

Page 12: Web技術勉強会 20110528

� 単独Mix-inオブジェクトを親にしたMix-inパターン

� var obj = Iphone.mix(Feature)

� obj.mix(Telephone)

� 単独Mix-inオブジェクトを子にした多重継承パターン

� var obj = Iphone.mix(Feature)

� Telephone.mix(obj, Android)� Telephone.mix(obj, Android)

� 多重継承オブジェクトを親にしたMix-inパターン

� var obj = Iphone.mix(Feature, Telephone)

� obj.mix(Android)

� 多重継承オブジェクトを子にした多重継承パターン

� var obj = Iphone.mix(Feature, Telephone)

� Android.mix(obj, Ipad)

Page 13: Web技術勉強会 20110528

実装方法について(__proto__の代用)

� Iphone.mix(Feature).mix(Telephone)

Iphone

parent()

getPhoneName()

Telephone

Feature

parent()

オブジェクト探索はプロトタイプチェーンをと同じこと。探索処理を自前で実装した。

� ①:子(Iphone#parent)は親(Feature)オブジェクトを指すようにする

� ②:①で一つのオブジェクトとして扱う

� ③:②のオブジェクト(parentの指す先を探し続ける)を探索して一番親のオブジェクト(Feature#parent)を見つけて、その親(Telephone)を指すようにする

Name()

parent()

getPhoneName()

getPhoneName()

Page 14: Web技術勉強会 20110528

� var obj = Iphone.mix(Feature).mix(Telephone)

� obj.getType() // Telephone#getType

実装方法について(プロトタイプチェーンの代用)

Telephone①

obj

parent()

getPhoneName()

④Feature

parent()

getPhone

Iphone

parent()

getPhoneName()

� ①:objにコピーする

� ②:子は親(祖先)を辿り、子にないメソッドがあればobjにコピーする。かつ、親はその親(祖先)を辿り、親にないメソッドがあれば親にコピーする。

� ③:親を辿り、すでに子にコピー済みのメソッドであればコピーしない

� ④:parent()はコピーしない(mix()も同様)

Telephone

parent()

getPhoneName()

②getType()

Name()

getType()

getPhoneName()

getType()

Name()

getType()

Page 15: Web技術勉強会 20110528

実装方法について(プロトタイプチェーンの代用)

� 子に親以上(祖先すべて)のメソッドをコピーする理由

� プロトタイプチェーンで辿れないのですべて子にコピーする

� こうすることで、呼び出し方はモダンブラウザと同じになる(内部処理は異なる)

� 祖先にしか存在しないメソッドを子にコピーしているので若干効率が悪いで若干効率が悪い

� IEではこうするしかないと判断(もっと良いやり方があれば…)

� IEは割り切るしかないかな、と。

� 現状ではIEとその他ブラウザで同じ動作をするようにまでなったのでひとまずよしとした。

Page 16: Web技術勉強会 20110528

前回未実装機能を実装

� 多重継承

� obj = Iphone.mix(Feature, Telephone);

� 実装自体は非常に簡単に完了

� 引数を可変にするだけ。arguments.length回同じことをするだけ。

Page 17: Web技術勉強会 20110528

Mix-in、多重継承したオブジェクトを再継承する機能

� Mix-inしたオブジェクトを継承対象にする

� obj = Iphone.mix(Feature).mix(Telephone);

� obj2 = Android.mix(obj);

� obj3 = obj2.mix(Ipad)

� 多重継承したオブジェクトを継承対象にする

� obj = Iphone.mix(Feature, Telephone);� obj = Iphone.mix(Feature, Telephone);

� obj2 = Android.mix(obj, Ipad);

� obj3 = obj.mix(Android, Ipad);

� 既存の継承機能を少し拡張することで対応。そんなに難しくなかった。

Page 18: Web技術勉強会 20110528

テスト

� QUnit

� jQueryのテスト用フレームワーク。現在はjQuery非依存のjsでもテスト可能。

� xUnitが分かればすぐ使える

� ローカルに落としてきて、test/index.htmlを実行するだけ

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

� 21項目、61個のテストを作成

� IE8,Chrome11ですべてパスを確認

Page 19: Web技術勉強会 20110528

公開について

� https://github.com/mapserver2007/mixjs

� 圧縮版も合わせて付属させる予定

� オプションとしてモジュールもCommitする予定

Page 20: Web技術勉強会 20110528

主要JavaScriptライブラリとの比較

jQueryjQueryjQueryjQuery prototype.jsprototype.jsprototype.jsprototype.js Ext.jsExt.jsExt.jsExt.js mix.jsmix.jsmix.jsmix.js

継承方法 プロパティ上書き

クラスベース クラスベース プロトタイプベース

親子関係の維持

☓ ☓ ☓ ○

親の呼び出し方

呼び出し不可

Super.prototype.getName.apply(this)

Super.superclass.getName.apply(this,

obj.parent().getName()

y(this) ply(this,arguments)

継承の仕方

var obj = $.extend(o1, o2)

var obj = Object.extend(cls1, cls2)

var obj =Ext.extend(cls1, cls2)

var obj = o1.mix(o2)

メリット シンプル 馴染みある継承方法

馴染みある継承方法

シンプル

デメリット

親を呼び出すことが出来ない

親の呼び出し方が長い

親の呼び出し方が長い

オブジェクトにmix, parentが実装される

Page 21: Web技術勉強会 20110528

まとめ

� 前回作成したコードをBrushUp

� バグはほぼない状態

� IE対応

� まずは今つくっているシステムに導入する予定

� 問題なさそうなら仕事でも使いたい�