node.jsでローカルディレクトリ配下のHTMLファイルのリストをSJISのCSVで出力するサンプル - Qiitaのサンプルソースを改変してたら、突然qのソース内でコールスタックのオーバーフローが発生しました。
/Users/hnakamur/mysrc/nodejs-html-filelist-csv-example/node_modules/q/q.js:126
throw e;
^
RangeError: Maximum call stack size exceeded
正しいソースは
var baseDir = process.argv[2],
baseURL = process.argv[3],
outFile = process.argv[4],
_ = require('underscore'),
fs = require('fs'),
path = require('path'),
Q = require('q'),
jschardet = require('jschardet'),
Iconv = require('iconv').Iconv,
cheerio = require('cheerio'),
csv = require('csv'),
scandir = require('scandir').create(),
officegen = require('officegen'),
htmlFileExpr = /\.html?$/i;
なんですが、コピペ後の編集ミスで
scandir = require('scandir').create();
officegen = require('officegen'),
とscandirの行の最後がカンマであるべきところがセミコロンになっていました。そのため、officegenとhtmlFileExprがグローバル変数になっていたわけです。それが、どうしてRangeError: Maximum call stack size exceeded
に繋がるのかはよくわかっていませんというか、ややこしそうなので考えていません。
で、原因箇所を特定するために、ソースコードのどこまで実行できたのかを調べようとして、あちこちconsole.log()でデバッグ出力を入れていたのですが、その時も1個のvarで編集の値を初期化していると編集が面倒でした。
var foo = doSomething(),
bar = doAnotherThing();
などとやってるとconsole.log()を入れるためにvarを分けてカンマをセミコロンに変えたりと面倒なわけです。
console.log('before doSomething');
var foo = doSomething();
console.log('after doSomething');
var bar = doAnotherThing();
console.log('after doAnotherThing');
最初から
var foo = doSomething();
var bar = doAnotherThing();
と書くか、varでは初期化せずに
var foo, bar;
foo = doSomething();
bar = doAnotherThing();
と書いておけば、楽だなと思いました。
フロントエンド用にブラウザで動くJavaScriptを書く時はminifyしたときになるべくファイルサイズが小さくなるようにしたいので、1つのvarで複数の変数を初期化する書き方にしたいのは理解できます。
でもNode.jsでJavaScriptを書く時は編集しやすさを優先しても良い気がします。同じJavaScriptで書き方を分けるのはイマイチな気もしますが、どうせ使える関数とか書き方も違うわけですし、Node.js v0.12が出たらyieldが使えるようになってますます違う書き方になるでしょうから、割りきっていい気がします。
Node.jsのソースを見ても https://github.com/joyent/node/blob/22c68fdc1dae40f0ed9c71a02f66e5b2c6353691/lib/stream.js#L24-L25 とか https://github.com/joyent/node/blob/22c68fdc1dae40f0ed9c71a02f66e5b2c6353691/lib/util.js#L32-L35 とかでvarは変数ごとに書いてますね。
ファイルの先頭のrequireが並ぶところはこの書き方がよさそうです。
JavaScript: The Good Partsでも書かれていますが、JavaScriptの場合は関数の途中にvarを書いても、その変数のスコープは関数の最初からになるので、varは関数の最初に固めて定義するほうが良いというのに私も賛成です。だったら、関数の中では最後の書き方でいいんじゃないかなーと思いました。