web-dev-qa-db-ja.com

Node.jsでモジュールレベルのreturnステートメントが機能するのはなぜですか?

私が答えていたとき 別の質問 トップレベルのreturnステートメントを持つNode.jsモジュールに出くわしました。例えば:

console.log("Trying to reach");
return;
console.log("dead code");

これは動作します エラーなし と出力します:

Trying to reach

標準出力ではなく "dead code "-returnは実際に実行を中止しました。

しかし ECMAScript 5.1のreturnステートメントの仕様 によれば、

セマンティクス

ECMAScriptプログラムはFunctionBody内にないreturnステートメントが含まれている場合、構文的に正しくないと見なされます。

上記のプログラムでは、returnは関数内にありません。

では、なぜこれがスローされないのですか?

48
thefourtheye

TL; DR

モジュールは、次のようにNode.jsによって関数内にラップされます。

_(function (exports, require, module, __filename, __dirname) {
    // our actual module code
});
_

したがって、上記のコードは実際には次のようにNode.jsによって実行されます

_(function (exports, require, module, __filename, __dirname) {
    console.log("Trying to reach");
    return;
    console.log("dead code");
});
_

そのため、プログラムは_Trying to reach_のみを出力し、returnステートメントに続く_console.log_をスキップします。

内部

ここで、Node.jsがモジュールを処理する方法を理解する必要があります。 Node.jsで.jsファイルを実行すると、Node.jsはそれをモジュールとして扱い、v8 JavaScriptエンジンでコンパイルします。

すべては runMain function で始まり、

_// bootstrap main module.
Module.runMain = function() {
  // Load the main module--the command line argument.
  Module._load(process.argv[1], null, true);
  // Handle any nextTicks added in the first tick of the program
  process._tickCallback();
};
_

_Module._load_ 関数では、 新しいモジュールオブジェクトが作成されます および 読み込まれます です。

_var module = new Module(filename, parent);
...
...
try {
  module.load(filename);
  hadException = false;
_

Module関数のloadはこれを行います

_// Given a file name, pass it to the proper extension handler.
Module.prototype.load = function(filename) {
  debug('load ' + JSON.stringify(filename) +
        ' for module ' + JSON.stringify(this.id));

  assert(!this.loaded);
  this.filename = filename;
  this.paths = Module._nodeModulePaths(path.dirname(filename));

  var extension = path.extname(filename) || '.js';
  if (!Module._extensions[extension]) extension = '.js';
  Module._extensions[extension](this, filename);
  this.loaded = true;
};
_

ファイルの拡張子はjsなので、_Module._extensions_が_.js_に対して何を持っているかがわかります。見ることができる ここ

_// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
  var content = fs.readFileSync(filename, 'utf8');
  module._compile(stripBOM(content), filename);
};
_

moduleオブジェクトの__compile_がその関数で呼び出され、 ここで魔法が発生します

_// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
_

ここで、ノードモジュールが使用する require 関数が最初に作成されます。

_function require(path) {
  return self.require(path);
}

require.resolve = function(request) {
  return Module._resolveFilename(request, self);
};

Object.defineProperty(require, 'paths', { get: function() {
  throw new Error('require.paths is removed. Use ' +
                  'node_modules folders, or the NODE_PATH ' +
                  'environment variable instead.');
}});

require.main = process.mainModule;

// Enable support to add extra extension types
require.extensions = Module._extensions;
require.registerExtension = function() {
  throw new Error('require.registerExtension() removed. Use ' +
                  'require.extensions instead.');
};

require.cache = Module._cache;
_

そして、コードをラップすることについて何かがあります、

_// create wrapper function
var wrapper = Module.wrap(content);
_

_Module.wrap_が何をするかを見つけることに着手しました これは他に何もありません

_Module.wrap = NativeModule.wrap;
_

これは_src/node.js_ファイルで定義されています で、これが見つかります、

_NativeModule.wrap = function(script) {
  return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};

NativeModule.wrapper = [
  '(function (exports, require, module, __filename, __dirname) { ',
  '\n});'
];
_

これは、プログラムがマジック変数exportsrequiremodule、___filename_および___dirname_

次に、ラップされた関数がコンパイルされて実行されます here with runInThisContext

_var compiledWrapper = runInThisContext(wrapper, { filename: filename });
_

そして最後に、モジュールのコンパイルされたラップされた関数オブジェクトが this のように呼び出され、値が exportsrequiremodule___filename_ および ___dirname_

_var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
_

これがモジュールがNode.jsによって処理および実行される方法であり、returnステートメントが失敗することなく機能するのはこのためです。

96
thefourtheye