やっと本題です。(長かったのは読んでいたからという言い訳を・・・)
cordovaのバージョンは4.2.0です。
まずJSファイルから読んでいきたいです。
device.jsが対象です。79行ぐらいですね。コードだけならもっと短いですね。
最初に個人的にAndroidが読むことができるのでAndroidでのコードリーディングを優先します。
device.js
plugins/org.apache.cordova.device/www/device.js
device.js 22
var argscheck = require('cordova/argscheck'),
channel = require('cordova/channel'),
utils = require('cordova/utils'),
exec = require('cordova/exec'),
cordova = require('cordova');
requireをしていますね。ファイル読み込みですかね??
個人的にJSから他のファイルを参照すること自体がないため、require自体が標準なのかを迷いました。
違いますね。標準ではないっぽい。
androidをplatformとして追加しておけばここにできるところに根幹ファイルがいらっしゃいますね。
platform/android/assets/www/cordova.js
cordova.js 57
require = function (id) {
if (!modules[id]) {
throw "module " + id + " not found";
} else if (id in inProgressModules) {
var cycle = requireStack.slice(inProgressModules[id]).join('->') + '->' + id;
throw "Cycle in require graph: " + cycle;
}
if (modules[id].factory) {
try {
inProgressModules[id] = requireStack.length;
requireStack.push(id);
return build(modules[id]);
} finally {
delete inProgressModules[id];
requireStack.pop();
}
}
return modules[id].exports;
};
これで読み込み処理をやっていますね。
正直読めません。このあたりで僕は挫折しそうですが。
まぁ、コードを追ってみて予想はできますので、予想してみます。
moduleにIDに合致する内容があるかどうかを判定して、IDにあった内容があればexportsで 使用できるようにしているのでしょう。
じゃあ、IDに合致するものはなにか??
requireといえば、ファイルがあるとイメージしますが、見当たりません。
google先生に「javascript require」で聞くと require.js がいっぱいヒットしますね。CommonJSとかも。
cordova.jsの中にすべてあるみたいです。
cordova/utils を構成しているのはこのあたりですかね。
cordova 1771
// file: src/common/utils.js
define("cordova/utils", function(require, exports, module) {
var utils = exports;
fileって書いてあるけど、「src/common/utils.js」 なんてファイル見当たらないです。
ネット経由でアクセスしているのかと疑いましたが、それっぽい記述もないのでここが該当するのではないかと。
defineもjavascriptにあったっけ?って思いましたが、ないよね。。。
defineもcordova.jsに書いてありますね。
cordova.js 77
define = function (id, factory) {
if (modules[id]) {
throw "module " + id + " already defined";
}
modules[id] = {
id: id,
factory: factory
};
};
呼び出し順としてはcordova.jsでdefineで宣言されてから、プラグインのファイル側でrequireでの呼び出しですね。
defineの内容としては、ID名の関数登録をしていますね。
factoryは関数を入れてますので、「cordova/utils」のIDで読みこめば、defineの時に作成しておいたクラスが関数が使えるみたいですね。
イメージ的にはクラスですね。
じゃあ、requireに戻ります。
正常系を通ったとすれば次のコードですかね。
inProgressModules[id] = requireStack.length; requireStack.push(id); return build(modules[id]);
まぁ、最後のbuild関数が重要な気しかしていません。ほかの処理は同じ宣言がされた時とか、エラーの時のための処理でしょう。
buildもcordova.jsに記載されていますね。
cordova.js 41
function build(module) {
var factory = module.factory,
localRequire = function (id) {
var resultantId = id;
//Its a relative path, so lop off the last portion and add the id (minus "./")
if (id.charAt(0) === ".") {
resultantId = module.id.slice(0, module.id.lastIndexOf(SEPARATOR)) + SEPARATOR + id.slice(2);
}
return require(resultantId);
};
module.exports = {};
delete module.factory;
factory(localRequire, module.exports, module);
return module.exports;
}
引数として渡されている内容は配列の{id,factory}ですね。
localRequireは「./」を消してしまう関数ですね。IDをしっかりと渡していれば問題なし。
module.exports = {}; でexports変数を初期化をしていますね。
factory(localRequire, module.exports, module); でやっとdefineで書かれていたfunctionの内容が呼び出されているわけですね。
返り値が「module.exports」ということは関数の中でmodule.exportsにいろいろした訳ですね。
ん??今思ったけど、javascriptってポインタ型??なのかな。
これだけみるとそうだな。
クラスって考えれば、そうか。factory型っていうのもうなずける。
デザインパターンのfactoryパターンも勉強しないとついていけないのか。。。近いうちにここだけ勉強するか。
cordova 1771
define("cordova/utils", function(require, exports, module) {
var utils = exports;
utils.defineGetterSetter = function(obj, key, getFunc, opt_setFunc) {
~~~
};
utils.defineGetter = utils.defineGetterSetter;
utils.arrayIndexOf = function(a, item) {
~~~
};
utils.arrayRemove = function(a, item) {
~~~
};
exportsの変数をutilsに代入していますね。というかポインタを合わせた??んですよね。
utilsに関数をいろいろ宣言していますね。これが使えるようになるってことですね。
requireはこれでイメージできるところまで来たかな。なんとか個人的には読めている。
疑問点がいっぱい
とりえあず長いので次回に延長。
あとこういう書き方できるんだと、すごい関心しています。JSは基礎がまだできていないと実感。
疑問点がいくつか出てきました。
- CommonJSとは?
- 関数の引数はポインタ渡し?
- デザインパターンのfactoryパターンかな
- この書き方のメリットがあまりわかっていない
わからないことはいづれ解消するようにします。
この記事、意外に長くなるのか。ということを自覚しました。
根幹のライブラリを読む必要が結構出てきますね。
まぁ、最初だけだと思いますので、頑張ってみます。
(あぁ、このテーマはソースコード書くのに適してないなぁ。探さないとまずい)