prototype.jsを読み解く

第1回 Prototypeライブラリ(1~197行目)

この記事を読むのに必要な時間:およそ 12 分

Prototype オブジェクト

0009: var Prototype = {
0010:   Version: '1.5.1.1',
0011: 
0012:   Browser: {
0013:     IE:     !!(window.attachEvent && !window.opera),
0014:     Opera:  !!window.opera,
0015:     WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
0016:     Gecko:  navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1
0017:   },
0018: 
0019:   BrowserFeatures: {
0020:     XPath: !!document.evaluate,
0021:     ElementExtensions: !!window.HTMLElement,
0022:     SpecificElementExtensions:
0023:       (document.createElement('div').__proto__ !==
0024:        document.createElement('form').__proto__)
0025:   },
0026: 
0027:   ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
0028:   JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
0029: 
0030:   emptyFunction: function() { },
0031:   K: function(x) { return x }
0032: }
0033: 

まず,Prototypeという名前のオブジェクトが生成されています。

以前はVersion,Kという属性くらいしかなかったのですが,現在ではVersion,Browser,BrowserFeatures,ScriptFragment,JSONFilter,emptyFunction,Kというプロパティが定義されています。

ドキュメント化されていなかったこともあり,このオブジェクト内にあるものは,どちらかというとprototype.js内部で使われることが多かったかと思いますが,現在では一部がドキュメント化されています。

9行目において,var Prototype = { ... } という形でリテラルオブジェクトとして生成されています。このPrototypeオブジェクトは,名前空間(namespace)として使うために生成されている,とのことなので,単にPrototypeライブラリ全体に関わる値を置いておくための場所,と考えればいいでしょう。

10行目ではPrototype.Versionを定義しています。これは文字列で,現バージョンでは '1.5.1.1' となっています。文字列形式だと数値比較がしづらかったりしますが,例えばscript.aculo.usでは以下のようにしてprototype.jsの古いバージョンを検出しています。

if (parseFloat(Prototype.Version.split(".")[0] + "." +
               Prototype.Version.split(".")[1]) < 1.5) {
  throw("script.aculo.us requires the Prototype JavaScript framework >= 1.5.0");
}

Prototypeライブラリの特定のバージョン以降を要求するような場合には,このような書き方をすればいいでしょう。

12行目からはPrototype.Browserオブジェクトの定義です。中身はいくつかのブラウザを判別するためのboolean値が入るようになっています。

例えばif (Prototype.Browser.IE) { ... } などとすることでIE環境で動作しているかどうかの確認が行えます。ここで,Prototype.Browser.IEではattachEvent()関数の存在を確認しつつ,Operaを除外するような定義になっています。

Prototype.Browser.Operaでは単純に単純にwindow.operaプロパティの存在を確認しているだけです。Prototype.Browser.WebKitではUserAgent文字列から 'AppleWebKit/' という文字列が存在するかどうかを確認しています。Safariだけではなく,WebKitを使った派生ブラウザ全体のための真偽値ですね。

Prototype.Browser.GeckoではUserAgent文字列に'Gecko'を含み,かつ'KHTML'を含まない,という条件になっています。これはSafariが'(KHTML,like Gecko)'という紛らわしい文字列をUserAgentに含んでいるためです。

19行目からはPrototype.BrowserFeaturesというオブジェクトです。ここではXPath,ElementExtensions,SpecificElementExtensionsという真偽値プロパティで,各機能が使えるかどうかをチェックできるようになっています。

Prototype.BrowserFeatures.XPathでは,document.evaluate関数が存在するかどうかを確認しています。これはDOM 3 XPathで定義されているインターフェイスで,Mozilla 系,Opera 9,Safari 3(ベータ版で確認)で実装されています(IEは7でもダメ⁠⁠。この真偽値によりDOM 3 XPathが使えるのかどうかがわかります。

Prototype.BrowserFeatures.ElementExtensions では,window.HTMLElementプロパティが存在しているかどうかを見ています。これは,DOM 2 HTMLのHTMLElementインターフェイスが実装されているかどうかを調べています。IE系は実装しておらず,Mozilla,Opera,Safariなどでは実装されています。ここを見て,Prorotypeライブラリ側でメソッドを提供する必要があるのか,ネイティブのメソッドを呼び出すことができるのかを判別しています。

Prototype.BrowserFeatures.SpecialElementExtensionsでは,div要素,form要素が個別のプロトタイプを持っているかどうかを調べています。!==演算子なので,異なるプロトタイプを持っていたらSpecialElementExtensionsは真となります。

真の場合,window.HTMLDivElement,window.HTMLFormElementインターフェイスを個別に持っていることになるので,後でElement.Methods.ByTagテーブルによって要素が拡張される際にそれらが利用されます。

27行目では,Prototype.ScriptFragmentとして正規表現で使うための文字列を定義しています。この文字列は,Stringオブジェクトの拡張部分で,script要素を取り除いたり,中身を取り出したりするために使われています。

28行目では,Prototype.JSONFilterとして,JSON文字列をフィルタするための正規表現オブジェクトを定義しています。単純にJSONを返すものをHTTPで取得できる場所に置いておくと,想定外の利用をされる可能性がありますが,それを避けるための目的で/* ... */で括った状態でJSONテキストを配置することがあります。Prototypeライブラリではこのときに/*-secure-JSON文字列*/ という形で置いておくと,自動的に外側の文字列を削って,適切なJavaScriptオブジェクトとして返す,ということをしてくれます。

30行目は何もしない関数Prototype.emptyFunction()です。これは関数が省略可能な引数として渡される際などに,デフォルトの値として使われます。以下の例では,state == 'Complete'として,this.options['onComplete']に関数が指定されていればそれを呼び出し,無ければ何もしないemptyFunction()を呼び出しています。このようにコードをシンプルに記述するために使われています。

(this.options['on' + state] || Prototype.emptyFunction)(transport, json);

31行目も似たような関数で,渡された引数をそのまま返す関数です。こちらはフィルタ系のコードでデフォルトの関数として使われていて,何も行いたい処理が無い場合にはこの関数を使って単に値を返すという形で記述されています。

著者プロフィール

栗山淳(くりやまじゅん)

S2ファクトリー株式会社株式会社イメージソース所属。
本業はWeb制作会社の裏方。得意分野はFreeBSDやPerlのはずだが,必要に迫られるとHTML/CSSやJavaScriptも書く。