【Node.js】Module Wrapper に遭遇して exports と module.exports の勘違いに気づいた
Node.js 初心者の自分は、なかなか require
(特に module.exports
) がイマイチ理解できない感あったので、いろいろ勉強してたら Module Wrapper に遭遇したので書き残しておきます。
参考
- Module Wrapper Function - YouTube
- javascript - How to change the Node.js module wrapper_ - Stack Overflow
そもそも require の実行の仕方が全然違った
他の言語からの勝手な想像で
- 基本的にはファイルを読み込んで展開してくれるから、普通に宣言したら どこからでも参照できそう
- むむむ...
module.exports
しないといけない様子... module.exports = app
は動くのにexports = app
は動かないぞ!
みたいな状態でした。
その勘違いを脱するヒントが Module Wrapper にありました。
どのように遭遇するか
v10.11.0
で試しています。
app.js
と module.js
を用意して実行してみるとエラーから Wrapper らしきものが見えます。
const m = require('./module.js');
err; // わざと ReferenceError を起こす
それを実行すると
$ node app.js
/Users/ight/module.js:1
(function (exports, require, module, __filename, __dirname) { err;
^
ReferenceError: err is not defined
at Object.<anonymous> (/Users/ight/module.js:1:63)
at Module._compile (internal/modules/cjs/loader.js:689:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Module.require (internal/modules/cjs/loader.js:637:17)
at require (internal/modules/cjs/helpers.js:20:18)
at Object.<anonymous> (/Users/ight/Sandbox/Node/app.js:1:73)
at Module._compile (internal/modules/cjs/loader.js:689:30)
ここで気になるのが (function (exports, require, module, __filename, __dirname) {
の部分です!
これは 即時関数の実行をしている ように見えますし、悩んでいた exports
と module.exports
の ヒントもありそう です。
Module Wrapper について
実は Node.js で require すると、function で wrap されて呼び出されます。
このことは Node.js Document にも書かれています。
Before a module's code is executed, Node.js will wrap it with a function wrapper that looks like the following:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
[Modules | Node.js v10.11.0 Documentation](https://nodejs.org/api/modules.html#modules_the_module_wrapper)
## exports しないと呼び出せない理由
function で wrap されていることを考えると、 **`export` していない変数が呼び出せないのは当然** で、Node.js は何も特殊なことをしてくれているわけではなさそうなことがわかってきました。
`require` ももう少し詳しく見ていくと仕組みが理解できそうです。
## exports? module.exports?
ここで気になるポイントが **引数に渡されている`exports` と `module`** です。`module` は
> module.exports is used for defining what a module exports and makes available through require().
`require` で利用可能なものを定義するために `module.exports` を使えばよく、`exports` は
> A reference to the module.exports that is shorter to type.
つまり、単に `module.exports` の参照という話でした。
しかし引数に渡されていることからわかるように、そのまま **`exports` に代入する書き方はできません** 。
```javascript
module.exports = app; // これは OK
exports = app; // これは exports の参照を書き換えているだけ
ちなみに
引数に require
が渡されているのも気になりましたが、いつか調べようと思います。
module.js 内で require('other_module.js')
をすると、引数の require が使われてまた 少し挙動が変わる のかなーとか、ただ 単に渡しているだけ なのかなとか。