1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Node.js】Module Wrapper に遭遇して exports と module.exports の勘違いに気づいた

Last updated at Posted at 2018-10-09

【Node.js】Module Wrapper に遭遇して exports と module.exports の勘違いに気づいた

Node.js 初心者の自分は、なかなか require (特に module.exports) がイマイチ理解できない感あったので、いろいろ勉強してたら Module Wrapper に遭遇したので書き残しておきます。

参考

そもそも require の実行の仕方が全然違った

他の言語からの勝手な想像で

  • 基本的にはファイルを読み込んで展開してくれるから、普通に宣言したら どこからでも参照できそう
  • むむむ... module.exports しないといけない様子...
  • module.exports = app は動くのに exports = app は動かないぞ!

みたいな状態でした。
その勘違いを脱するヒントが Module Wrapper にありました。

どのように遭遇するか

v10.11.0 で試しています。
app.jsmodule.js を用意して実行してみるとエラーから Wrapper らしきものが見えます。

app.js
const m = require('./module.js');
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) { の部分です!
これは 即時関数の実行をしている ように見えますし、悩んでいた exportsmodule.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 が使われてまた 少し挙動が変わる のかなーとか、ただ 単に渡しているだけ なのかなとか。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?