著作一覧 |
Chrome上で動かしている割と大きなシステムがあって、そのうちバックグラウンド側で動く(つまるところ、モデルということだが)スクリプト群に、コマンドラインインターフェイスを付けて動かしたくなった。
で、NodeJSを使う(同じV8だし)ことは決めて、7月ごろにインフラ用の環境を用意しておいたのだが、やっと手が空いたのでいじろうとして仰天した。
NodeJSのWebサイトには、LTSが4.4と書いてあるのに、環境は0.12とか出てくるじゃん。
で、NEWSを見たらいきなり4になったということで納得して(V8 v4.5の4なのか?)、さあ始めるかと始めたところ、ひっかかる。
元のスクリプト群が、リソースを突っ込むためのオブジェクトresを共有しているわけだが、すべてのスクリプトは次のように書いてある。
var res = res || {}; (function() { res.thismodule = {...}; })();
当然のように、NodeJS用に作ったローダースクリプトで、次のように書いた。
global.window = { alert: function(s) { console.log(s); } }; global.window.parent = global.window; global.res = {}; global.window.res = global.res; require('./foo.js'); ...
でも何か動作がおかしい。それで何が起きているか見てみると
global.res = {}; require('./foo.js'); console.log(res); //=> {} ...
はて?
で、要するにvar res = (右辺を最初に評価してグローバルなresを見る)res || {};
と考えていたら、var res = (まず全体を評価しているのでこれはvar resで宣言された)res || {};
ということのようだ。(requireされたファイルのグローバルはモジュールに閉じられる)
「ということのようだ」は気持ち悪いのでまじめに調べると
A variable with an Initialiser is assigned the value of its AssignmentExpression when the VariableStatement is executed, not when the variable is created.
12.2 Variable Statement
なので、「右辺がついた変数の代入式の値設定は、変数の作成時ではなく、var宣言の実行時に行われる」。ということは、宣言したこのスコープでの空の変数が右辺に出現している扱いとなる、「ということのようだ」。
いずれにしても、しょうがないので、globalのスコープになるように自分でevalすることにして解決。
function load(s) { var buff = fs.readFileSync(s); eval.apply(global, buff.toString()); } global.res = {}; load('./foo.js'); ...
もっとうまい方法があれば知りたいところだ。
キャンベル V8 野菜ジュース 163ml×6個(-) マッドマックス 怒りのデス・ロード(字幕版)(トム・ハーディー) Node.jsビギナーズガイド: サーバーサイドJavaScriptをマスターせよ! PRIMERシリーズ (libroブックス)(掌田津耶乃)TODO: 瀧内さんからmodule.exportsを使うというのを教えて頂いたので、試す。
追記:結局vm.runInContextを使うことにした。
ジェズイットを見習え |