Node.jsのモジュール機能について



Node.jsのモジュール機能について学習した。
モジュール機能は、サーバーサイドJavaScriptの標準APIの仕様を定めるプロジェクトであるCommonJSでも、早い時期から規格化と実装が進んでいたようだ。
モジュールは、プログラムのソース・ファイルを複数ファイルに分けて管理し、名前空間を分離することを可能にする、Node.js自体が提供する言語機能である。

Nodeのバージョンは、

$ node -v

v10.9.0

モジュールのエクスポートとインポート

下記の構成で、2つのファイルを使って検証した。

$ pwd

/Users/user/Desktop/node_test
$ ls 

Animal.js main.js

まずは、モジュールとして利用される側。
moduleという名前のオブジェクトが、最初から存在する。
moduleオブジェクトのexportsプロパティが参照するオブジェクトのプロパティに、エクスポートするオブジェクトを設定することで、オブジェクトをエクスポートできる。

Animal.js
var Animal = function() {}
module.exports.Animal = Animal;

モジュールとして利用する側は、require関数を使う。
require関数の引数には、ファイル名を指定する。ファイル名の.jsは省略できる。

main.js
var lib = require('./Animal')
var animal = new lib.Animal();

当然下記のようにも書ける。

var Animal = require('./Animal').Animal;
var animal = new Animal();

実行すると、特にエラーなく処理が終了する。


$ node main.js 


モジュールは、デフォルトでグローバルの名前空間を汚染しない。

下記のように、エクスポートしない場合は、別のファイルから見えない。

Animal.js
var Animal = function() {}
// module.exports.Animal = Animal;

$ node main.js
TypeError: lib.Animal is not a constructor

module以外に、exportsという名前のオブジェクトも、最初から存在する。
下記のコードでtrueが出力されることが示すように、module.exportsとexportsは、同じオブジェクトを参照している。

console.log(module.exports === exports)

従って、下記のようにも書ける。

Animal.js
exports.Animal = Animal;


moduleオブジェクト

console.debugで、moduleオブジェクトについて、詳しく見てみた。


Animal.js
var Animal = function() {}
module.exports.Animal = Animal;
console.debug(module)

$ node main.js
Module {
  id: '/Users/user/Desktop/node_test/Animal.js',
  exports: { Animal: [Function: Animal] },
  parent:
   Module {
     id: '.',
     exports: {},
     parent: null,
     filename: '/Users/user/Desktop/node_test/main.js',
     loaded: false,
     children: [ [Circular] ],
     paths:
      [ '/Users/user/Desktop/node_test/node_modules',
        '/Users/user/Desktop/node_modules',
        '/Users/user/node_modules',
        '/Users/node_modules',
        '/node_modules' ] },
  filename: '/Users/user/Desktop/node_test/Animal.js',
  loaded: false,
  children: [],
  paths:
   [ '/Users/user/Desktop/node_test/node_modules',
     '/Users/user/Desktop/node_modules',
     '/Users/user/node_modules',
     '/Users/node_modules',

     '/node_modules' ] }

moduleオブジェクトのプロパティが一覧された。

exportsプロパティの存在が確認できる。

parentプロパティには、モジュールを呼び出している側のmoduleオブジェクトが格納されるようだ。

pathsプロパティには、モジュールを探すパスの配列が設定されているようだ。
基本的に、node_modulesというディレクトリーを親ディレクトリーに向かって探していくようだ。
注目すべきは、pathsプロパティの配列に、カレントディレクトリーが含まれていないことである。
よって、下記のコードはエラーとなる。

var Animal = require('Animal').Animal;

$ node main.js

Error: Cannot find module 'Animal'

下記の構成にすれば、node_modulesはモジュールを探すパスであるため、上記のコードでも問題ない。

.
├── main.js
└── node_modules

    └── Animal.js

require関数

console.debugで、require関数について、詳しく見てみた。

main.js
var Animal = require('Animal').Animal;
var animal = new Animal();
console.debug(require)

$ node main.js
{ [Function: require]
  resolve: { [Function: resolve] paths: [Function: paths] },
  main:
   Module {
     id: '.',
     exports: {},
     parent: null,
     filename: '/Users/user/Desktop/node_test/main.js',
     loaded: false,
     children: [ [Module] ],
     paths:
      [ '/Users/user/Desktop/node_test/node_modules',
        '/Users/user/Desktop/node_modules',
        '/Users/user/node_modules',
        '/Users/node_modules',
        '/node_modules' ] },
  extensions:
   { '.js': [Function], '.json': [Function], '.node': [Function] },
  cache:
   { '/Users/user/Desktop/node_test/main.js':
      Module {
        id: '.',
        exports: {},
        parent: null,
        filename: '/Users/user/Desktop/node_test/main.js',
        loaded: false,
        children: [Array],
        paths: [Array] },
     '/Users/user/Desktop/node_test/node_modules/Animal.js':
      Module {
        id: '/Users/user/Desktop/node_test/node_modules/Animal.js',
        exports: [Object],
        parent: [Module],
        filename: '/Users/user/Desktop/node_test/node_modules/Animal.js',
        loaded: true,
        children: [],

        paths: [Array] } } }

require関数のプロパティが一覧された。

mainプロパティは、メインとして実行されたスクリプトのmoduleオブジェクトが格納されるようだ。
よって、下記のコードは、メインとして実行されたスクリプトではtrueとなる。

main.js
console.debug(require.main === module)

$ node main.js 

true

利用されるモジュール側でも確認すると、確かにrequire.mainには、メインとして実行されたスクリプトであるmain.jsのmoduleオブジェクトが格納されている。

Animal.js
var Animal = function() {}
module.exports.Animal = Animal;
console.debug(require.main === module)
console.debug(require)

$ node main.js
false

{ [Function: require]
  resolve: { [Function: resolve] paths: [Function: paths] },
  main:
   Module {
     id: '.',
     exports: {},
     parent: null,
     filename: '/Users/user/Desktop/node_test/main.js',
     loaded: false,
     children: [ [Module] ],
     paths:
      [ '/Users/user/Desktop/node_test/node_modules',
        '/Users/user/Desktop/node_modules',
        '/Users/user/node_modules',
        '/Users/node_modules',
        '/node_modules' ] },

  extensions:
以下省略...

このように、メインとして実行されたスクリプトを特定できる。




コメント

このブログの人気の投稿

PowerShell 6で、Shift_JISのCSVをImport-Csvで読み込んだら文字化けした

Windowsで、特定のユーザーに特定のサービスの再起動を許可する

PowerShellでイベントログを取得する時、「指定した選択条件に一致するイベントが見つかりませんでした。」が煩わしいのでcatchする