Node.jsのmoduleとexports、そしてCoffeeScript
ここしばらく、Node.jsを使った開発で忙しくて、ようやく一段落つきました。ということで随分とNodeを使ってきたつもりだったのですが、肝心のexportsの辺りの理解が浅かったことに気づいたので、少しまとめます。
exportsとmodule.exports
exportsとmodule.exportsについては、少々バージョンが古い*1ですが
Node.js : exports と module.exports の違い(解説編) - ぼちぼち日記
が非常に参考になりそうです。まだ詳細は理解できてませんが、一先ずmodule.exportsが強いということが分かれば良いのではないかと思います。
exportsのスニペット*2
ここで紹介するのは、Node.jsとCommon.jsとDOMに対応したexport方法です。underscore.jsのコードを参考、というかほぼそのままです。
ここではtestをexportsすることにします。
test = {foo: "foo", bar: "bar"}; if (typeof exports !== "undefined") { if (typeof module !== "undefined" && module.exports) exports = module.exports = test // Node.js exports.test = test // Common.js } else { this.test = test // DOM }
Nodeの時は、module.exportsに入るtestがエクスポートされるオブジェクトになりますが、if文を出たところでtest.test = test
となるので、Common.js形式でもエクスポートされているという仕組みです。
CoffeeScriptでの注意点とシンプルなハック
CoffeeScriptでは少し注意が必要です。
まず、上記のコードを普通にCoffeeScriptで書いてみます。
test = foo: "foo", bar: "bar" if exports? if module? and module.exports? exports = module.exports = test exports.test = test else @test = test
これの変換結果は
var exports, test; test = { foo: "foo", bar: "bar" }; if (typeof exports !== "undefined" && exports !== null) { if ((typeof module !== "undefined" && module !== null) && (module.exports != null)) { exports = module.exports = test; } exports.test = test; } else { this.test = test; }
となる*3わけですが、exportsへの代入処理があるために、exportsがローカル(var)で宣言されてしまいます。その結果、一番目のifがfalseになるので、DOMの方が実行されることになります。これではいけません。
そこで、簡単な解決法を考えました。
test = foo: "foo", bar: "bar" if exports? if module? and module.exports? `exports = module.exports = test` exports.test = test else @test = test
exportsに代入する部分のみを埋め込みJavaScriptの形にしただけですが、これでvar exports
が現れることはなくなります。
一件落着。