はじめに

毎日新しいJavaScriptライブラリが登場していると思いますが、それらがどういう実装になっているかを知ることはライブラリを使う以上に大事かもしれません。

ソースを全部読めば分かるかもしれませんが、それをやるには時間が足りません。

JavaScriptに限った話では無いですが、今回はJavaScriptを例に”特定の機能はどうやってるんだろ?”という事を調べる方法についてです。

探す前にドキュメントに載ってないかを見るのが手間がなくて一番よいですが、書いてない場合は実装を見ます。

Vue.js

今回はVue.jsというAngularJSやKnockoutのようなViewとModelのデータバインディグを行うライブラリを例に、データバインディグはどうやっているのかを2つの方法で調べてみたいと思います。

コードをステップで見ていく

これはよく見る方法で皆さんもやったことがあると思います。

見たい挙動(今回はモデルへのバインディグ)のサンプルコードを書いて、
そこに対してブレークポイントを貼り step in しながら目的の実装を探すという単純な方法です。

トップページにあるサンプルコードを見ても分かるように、new Vue({ /*モデル*/ }) となってるので、Vueコンストラクタの実装を見ていけばいいことがわかります。

var demo = new Vue({// < - breakpoint 
    el: '#demo',
    data: {
        message: 'Hello Vue.js!'
    }
})
</span>

サンプルコードを書いて new Vueの部分にブレークポイントを貼るだけで準備完了です。

後は、デバッガーのステップ実行しながら、怪しい関数等を見ていくだけです(今回ならObserveとかbindingとかの用語を見ていけば多分見つかるはずです)

以下は実際に見ていった動画です

勘であてる

もう一つ短時間で探す方法として、その機能(今回はデータバインディグ)に使われそうなメソッドや名前で検索してしまう方法です。

今回はデータバインディグがキーワードです。

また、Browser Supportを見るとES5サポートしている事が前提となってることがわかります。

Vue.js supports most ECMAScript 5 compliant browsers, essentially IE9+. IE8 and below are not supported.

ここで、ピンと来る人もいると思いますが他のところもチェックすると、サンプルコードを見てもknockout.jsのように値の変更を監視するためにラッパーをかぶせたりはしていないように見えます。

knockoutの場合は監視したいプロパティに ko.observable(value) のようなラップが必要になります。

この情報から推測するとJavaScriptネイティブのgetter/setterを使ってるんじゃないかと気付きます。
getter/setterを設定(他にも方法はありますが)するObject.definePropertyはES5からサポートされているので、ES5をサポートしてないブラウザはサポート外になってるんじゃないかという感じですね。

探すポイントは推測できたので実際に検索してみます。


(こっから先、普通に文字列検索してもいいと思うので若干蛇足気味です)

普通にgrep的な検索でもいいですが、JavaScriptを例にしたのでgraspを使ってみます。(多分このケースだと普通に文字列検索した方が楽です…)

graspは単純な文字列検索ではなくて、JavaScriptの構文を元に検索/置換が出来るコマンドラインツールです。

以下でも軽く紹介してるので見てみるといいかもしれません。

$ npm install -g grasp

でインストールしておきます。

Object.definePropertyを呼び出し箇所を検索

Object.defineProperty

Object.defineProperty(obj, prop, descriptor)

のような構文なので、上記のように呼び出してる場所を検索したいと思います。

Object.defineProperty(obj, prop, descriptor)に該当するものを検索する場合、
graspでは以下のように書くことが出来ます。

grasp -e 'Object.defineProperty(__,__,__)' src/*

一つづつ見ていくと、

  • graspコマンドで src/* 以下のファイルを検索しています
  • -eExample Queryを使うというオプションです。
    • graspはExample QuerySelector Queryの2つの方法で検索が出来ます
    • ワイルドカード的なものとCSSセレクタ的なもので検索する方法です
  • 'Object.defineProperty(__,__,__)' は検索したいコードです
    • __ というのが引数の数(3つ)あることがわかります。
    • __ はワイルドカードの事を示しています
    • 正規表現なら 'Object.defineProperty(.*?,.*?,.*?)' みたいな感じ?
    • 正規表現と違って、ホワイトスペースや改行等余計な事を意識しなくても書くことが出来ます。

その結果を見ると幾つか使ってる箇所がわかります。

Vue  zsh 2014 02 08 23 19 26

後は、見つけた場所を実際に見ていくといった感じです。

Object.definePropertyのaliasを検索する

今回は直接 Object.defineProperty を使っていたので問題無いですが、
Object.defineProperty は長いのでライブラリの内部では以下のようなエイリアスを貼って使っているかもしれません。

var def = Object.defineProperty;

“Object.defineProperty” という文字列を検索すれば、見つけることも出来ますが、graspではもっと具体的に、”varである変数にObject.definePropertyを代入している所” というのも検索出来ます。

正規表現でも出来そうに見えますが、以下のようなコードだと結構難易度が上がります。

var slice = Array.slice,
    def = // for getter/setter
     Object.defineProperty;

graspだと、以下のようにして検索することが出来ます。

grasp -e '__[init=Object.defineProperty]' test.js

結果を見るとちゃんと該当する部分を検索出来てる事がわかります。

Vue  zsh 2014 02 08 23 34 49

検索できたので、どういう風に検索されているかを見ていきます(–debugオプションとかもあるのでそちらも見るといいかもしれないですね)

  • -e__ は先ほど同じ意味で、Example Queryとワイルドカードです

__[init=Object.defineProperty] というのは、CSSセレクタをイメージすると分かりやすいです。

要素は__なので何でもよいですが、ここでいう要素はJavaScript Syntax | Grasp – JavaScript structural search, replace, and refactorで説明されているJavaScript ASTの要素の事を言っています。

var a; のような変数宣言は、 VariableDeclarator という要素(var-dec)に該当します。(上記の例はワイルドカードがこれに該当してる)

そしてVariableDeclaratorというのは init というプロパティ(属性)を持っていて、この initの値が Object.defineProperty にマッチするものを検索しています。

(init の値というは簡単に言えば、 var a = 1; の 1の部分)

この辺はコードをパースした結果を見比べて見るといいかもしれません。

graspはSelector QueryというCSSセレクタ的なものもあるので、こちらを使った場合は直感的では無いですが以下のように書くことが出来ます。

grasp -s 'var-dec[init=member[obj=#Object][prop=(#defineProperty)]]' test.js

今回はES5サポートというキーワードを元に安全で効率も良いgetter/setterを使ってると推測して検索しましたが、
キーワードが絞れない場合は実際にステップ実行を見ていったほうが楽だと思います。
古いIEをサポートしてるなら他の方法が幾つかありそうでキーワードから考えるのは中々難しい。

例えば、Ractive.jsのようにArray modification · RactiveJS/Ractive Wikiハックを使ったり、AngularJSはDirty checkingというやり方をしている等やり方的に幾つかでてくると思うので、
ステップ実行やドキュメントから探したりした方が効率が良さそうです。

その他

今回紹介した方法以外にも、最近のライブラリならある程度モジュールでファイルが別れてるはずなので、ファイル名をヒントに探すとか、
GithubやStackOverFlowで検索するとかやり口はいくらでもあります。

今回ターゲットにしたVue.jsもCommonnJSで書かれていて、componentを使ってファイルをビルド出来るようにしてました。
(ビルド環境も面白くて、Gruntを使っている感じでしたが、内部でgulp pluginであるgulp-componentを使ったりしてた)

おわりに

この記事では、JavaScriptのライブラリの実装を見ていく2つの方法について書きました。

  • デバッガーを使ってステップ実行をして中身を見ていく方法
  • graspを使ってキーワードから探していく方法

JavaScriptでは公開されてるライブラリも含め、普通のウェブサイトでもソースコードが見られてので、実際に動いてるソースが探せば見つけやすい世界です。

Webkit/Chrome/Firefox(27〜)のDevToolsではJavaScriptの整形表示機能もあったりします。

ソースコードから学べることも多いので、興味があるものは色々読んでみるといいかもしれません。