WEB+DB press vol.59に載っていた、id:uupaaさんのナビ子記法が目からウロコだったので真似してみた。
(function(global, require) { var sys = require('sys'), fs = require('fs'), Script = process.binding('evals').Script; setups = [], events = {}; // public interface global.redflag = { event: _event, setup: _setup }; // implementation function _event(name, func) { events[name] = func; } function _setup(func) { setups.push(func); } function processFile(file) { var data = fs.readFileSync(file); Script.runInThisContext(data); } function doEvent(name, event) { var env = {}; doSetups(env); if ( event(env) ) { sys.print("ALERT: " + name + "\n"); } } function eachEvent(func) { for ( name in events ) { func(name, events[name]); } } function doSetups(env) { var i = 0, len = setups.length; for (; i < len; ++i) { setups[i](env); } } function processFiles(err, files) { var i = 0, fileNum = files.length; if ( err ) { console.log(err); } else { for (; i < fileNum; ++i) { if ( /events\.js$/.test(files[i]) ) { processFile(files[i]); } } eachEvent(doEvent); } } fs.readdir('./', processFiles); })(global, require);
APIを変えたので、クライアントコードの側も変わる。
redflag.event('hyde is smaller than 157cm!', function(env) { return env.hyde < 157; }); redflag.setup(function(env) { env.hyde = 156; });
あとこれ自体はナビ子記法ではないのだけど、関数の先頭に変数宣言をまとめてしまうのはよい習慣だと思う。「forループの中で使う変数をforループの外で宣言してしまうのはバッドノウハウなのではないか」と思う人がいるかもしれないが、JavaScriptにはブロックスコープが存在しない(実際はJavaScript 1.7で導入されるletがあればいけるが、世界中の多くの人は1.7の動かないブラウザを使っている)ので、あたかもブロックスコープがあるようにブロックの内側に変数を定義してしまうより、潔く外側に出してしまった方がいい。少なくとも今は。
実際、「ブロックスコープはないんだ」ということが頭では一応分っているつもりの僕でも、たまにブロックスコープがあるかのような書き方をしてしまうことがある。人間誰しもミスをする。「この変数たちはこの関数内ではグローバルですよ」とはっきり示しておき、そういった余計なミスを減らす意味でも、変数宣言は関数の最初にまとめてしまった方がよいと思われる。