Hatena::Groupmoz-addon

hogezilla RSSフィード

当ページに書かれているコードは、修正BSDライセンスのもと、再頒布して頂いて構いません。

 | 

2012-01-22

「拡張機能で安全にArrayをprototype拡張」の調査

| 22:01 | はてなブックマーク - 「拡張機能で安全にArrayをprototype拡張」の調査 - hogezilla

前回は失敗に終わったわけだが、何故なのかさっぱりなので、もう少し調査した。

調査したといっても解決はできていないが...

前回のあらすじ

  • Array.prototypeを拡張して、使い勝手を良くしたい
  • しかし、他の拡張に迷惑をかけるわけにはいかないので、普通にArray.prototypeを拡張するのはNG

ということで、独自のコンテキストを持つ JavaScript コードモジュール内で Array を拡張し、エクスポートしてあげれば良いのではないか、と考えた。

が、しかし、実際にやってみると、一部のメソッドの戻り値が、拡張したprototypeではなく、元の window.Array 側の prototype になってしまっていた。

よって、計画は失敗に終わった。

prototypeが変わってしまうメソッド

以下のメソッドを使用すると戻り値の配列が拡張したprototypeにならない。

  • slice
  • splice
  • filter
  • map
  • concat
ECMAScriptの仕様

上記メソッドの詳細をECMAScriptの仕様から見てみると、全て、新たなArrayを生成して返していることが分かる。

Let A be a new array created as if by the expression new Array() where Array is the standard built-in constructor with that name.

といった感じで、配列Aを作り、返している。

では、the standard built-in constructorは、組み込みのコンストラクタだと言っているのでArrayそのものだろう。そして、new Arrayとしているので、そのプロトタイプは引き継がれるはずだ。

しかし、このコンストラクタJavaScriptコードモジュール上のArrayなのか、元のグローバルオブジェクト(window)のものなのか...。

グローバルオブジェクト

Firefox上だとグローバルオブジェクトとなるものには3種類ほどある。

window
DOMWindowであり、もっとも使われるグローバルオブジェクト
Sandbox
Components.utils.Sandboxから作ったり、BootstrappedExtensionのグローバルオブジェクトであったりする
BackgroundStage
JavaScriptコードモジュールのグローバルオブジェクト、また、XPCShellのグローバルオブジェクトもこれ

これらに違いはあるか。場合によっては組み込みコンストラクタを使用するところが違うのではないか、と思い、それぞれのグローバルオブジェクトから拡張Arrayをimportして使用してみた。

全て以下の様な読み込み方法を使用した

var jsm = {};
Components.utils.import("resource://..../array.jsm", jsm);
var a = ["a","b","c","b"];
var b = jsm.Array(a);
b instanceof Array;
b instanceof jsm.Array;

var c = b.slice();
c instanceof Array;     // うまく行っていれば、false
c instanceof jsm.Array; // うまく行っていれば、true
//....
読み込み箇所 結果 備考
Windowから × XULな拡張のoverlayからscript要素を挟んでimport
Sandboxから BootstrappedExtensionからimport
BackgroundStageから × 別コードモジュールからarray.jsmをimport

結果、BootstrappedExtensionのSandbox上からなら、Arrayコンストラクタは読み込んだコードモジュール上のものが使用されることが分かった。

const Cu = Components.utils;
var sandbox = Cu.Sandbox(window);
Cu.evalInSandbox('Components.utils.import("resource://.../array.jsm")', sandbox);

var a = Cu.evalInSandbox('Array(["a","b","c","b"]).slice().uniq()', sandbox) 

などと、サンドボックスを作って、その上で実行する限りはimportしたArrayのコンストラクタが使われていることも確認できた。

さて、このどのコンストラクタが使われるかというので違いがあるのは、仕様なのか、それとも...

トラックバック - http://moz-addon.g.hatena.ne.jp/teramako/20120122
 |