2007/04/17

FW prototype.jsのObject汚染を回避する方法

prototype.jsのObject汚染を回避する方法

かなりターゲットの狭いTips。役に立たない。

prototype.jsと いうRuby on Railsなんかのフレームワークで使われている有名なJavaScriptのライブラリがあって、これが色々と使えそうな処理を綺麗に詰め込んであり、 デファクトスタンダート的な地位を確立しているのだけれど、ちょっと微妙だなーと思うところがあって、それはObject.prototypeを拡張してしまう点。

実際の弊害はこういう。
http://d.hatena.ne.jp/nazoking/20050425/1114374966

要は連想配列として使うときに困るって話。
多分prototype.jsはJavaScriptの側でロジックを組むことをあまり想定していないため、この辺の問題にあんまり配慮していないのではないかと思うのだけれど、とりあえず無理やり回避する方法を思いついたので書いてみる。

http://la.ma.la/misc/js/prototype.html

IFRAMEを作ってやって、その中でObjectやArrayなんかを作成してやれば、何にも拡張されていない素の状態のオブジェクトを返すことが出来る、という具合。

どっちにしろ面倒くさいので微妙、使う機会があるのかどうか。
もっと真っ当な方法があるのかも知れない。

----
ついでだから後二つぐらいTipsを。

インストールされているフォント一覧取得(IE限定)
http://la.ma.la/misc/js/fontbrowse.html

バックグラウンドで一覧を勝手に収集して、フォントの普及率を調査するページとか作れそうだな、とか、考えたことがあった。


マウスのホイール操作を検知する(IEとFirefox限定)
http://la.ma.la/misc/js/wheel.html

あんまり使われてない気がするけど、JavaScriptでマウスのホイールイベントを拾ってくることが出来る。IEの場合はonmousewheel、Firefoxの場合はDOMMouseScrollというイベントハンドラが使える。IEとFirefoxで使えればカバー範囲は十分広いと思うので、もっと積極的に使っても良い気がする。


----
追記 07/12

一応書いておくと、これは「IFRAMEを使うことで別のJavaScript実行環境を作れる」という、いわゆるネタです。ビルトインオブジェクトをオーバーライドすると便利なんだけども影響が気になる、という時に使えるかもしれない。
上記のサンプルでも使っているけど、普通のやり方だとprototypeに含まれている要素を除外して処理する関数を作ったり、メソッドを加えてやれば良いと思う。

多分こんな感じ。
    Object.prototype.forEach = function(func){
for(var key in this){
if(!(key in this.constructor.prototype)){
func(this[key],key,this)
}else if(this[key] != this.constructor.prototype[key]){
func(this[key],key,this)
}
}
};
obj.forEach(function(value,key,self){
alert([value,key]);
});

で、これをやるとますますObject.prototypeが汚染される上、
yomi["forEach"] = "ふぉーいーち"
という連想配列に対してはforEachメソッドが使えなくなるという罠もある。

自前のクラスを作るにしても全部Objectオブジェクトがベースになるので、個人的にはObjectはなるべくプレーンな状態で残しておいた方が望ましいと思う。
ただ、prototype.jsに関してはObject.extend()一つでコード量を大幅に減らせているメリットの方が大きいと思うので、その恩恵を受けつつもプレーンな状態のObjectを作成するにはIFRAME内で実行するぐらいしか思いつかなかった、という話。

----
さらに追記

プロパティが、そのインスタンスに固有なのか、クラスに元々存在しているのかを調べるにはhasOwnPropertyというのを使えば良いらしい。

http://www.interq.or.jp/student/exeal/dss/ref/jscript/object/objects.html#hasOwnProperty

上記のコードはこれで良さそう。
prototypeに含まれていない要素のみ回すforEach。
    Object.prototype.forEach = function(func){
for(var key in this){
if(this.hasOwnProperty(key)){
func(this[key],key,this)
}
}
};


オブジェクト指向良くわかってないので用語の使い方とか怪しいかもしれない。

No comments: