Ответ 1
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
и пропускает console.log
в соответствии с инструкцией return
.
Внутренности
Здесь нам нужно понять, как Node.js обрабатывает модули. Когда вы запускаете свой .js файл с помощью Node.js, он рассматривает это как модуль и компилирует его с помощью механизма JavaScript v8.
Все начинается с 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
функция, используемая нашими модулями node.
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
does, которое не что иное, как
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});'
];
Так наши программы имеют доступ к магическим переменным, exports
, require
, Module
, __filename
и __dirname
Затем завернутая функция скомпилирована и выполнена здесь с runInThisContext
,
var compiledWrapper = runInThisContext(wrapper, { filename: filename });
И затем, наконец, модуль, скомпилированный завернутый объект функции, вызывается как this, со значениями, заполненными для exports
, require
, Module
, __filename
и __dirname
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
Так обрабатываются и выполняются наши модули с помощью Node.js, поэтому оператор return
работает без сбоев.