为什么模块级返回语句在 Node.js 中起作用?
当我回答 另一个问题时,我遇到了一个带有顶级 return 的 Node.js 模块
语句.例如:
When I was answering another question I came across a Node.js module with a top-level return
statement. For example:
console.log("Trying to reach");
return;
console.log("dead code");
这工作没有任何错误并打印:
Trying to reach
在标准输出中但不是死代码
"- return
实际上已经停止执行.
in the standard output but not "dead code
" - the return
actually ceased execution.
但根据 ECMAScript 5.1 中 return
语句的规范,
But according to the specification of return
statements in ECMAScript 5.1,
语义
如果一个 ECMAScript 程序包含一个不在 FunctionBody
中的 return 语句,则认为它在语法上是不正确的.
An ECMAScript program is considered syntactically incorrect if it contains a return statement that is not within a FunctionBody
.
在上面显示的程序中,return
不在任何函数中.
In the program shown above return
is not within any function.
那为什么不扔呢?
推荐答案
TL;DR
模块由 Node.js 封装在一个函数中,如下所示:
The modules are wrapped by Node.js within a function, like this:
(function (exports, require, module, __filename, __dirname) {
// our actual module code
});
所以上面显示的代码实际上是由Node.js执行的,像这样
So the above shown code is actually executed by Node.js, like this
(function (exports, require, module, __filename, __dirname) {
console.log("Trying to reach");
return;
console.log("dead code");
});
这就是为什么程序只打印 Trying to reach
并跳过 return
语句之后的 console.log
的原因.
That is why the program prints only Trying to reach
and skips the console.log
following the return
statement.
这是我们需要了解 Node.js 如何处理模块的地方.当您使用 Node.js 运行 .js 文件时,它会将其视为一个模块并使用 v8 JavaScript 引擎对其进行编译.
This is where we need to understand how Node.js processes Modules. When you run your .js file with Node.js, it treats that as a module and compiles it with the v8 JavaScript engine.
这一切都始于 runMain
函数一个>,
It all starts with 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
这样做,
The Module
function's load
does this,
// 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
有什么.可以看到这里
Since our file's extension is js
, we see what the Module._extensions
has for .js
. It can be seen here
// Native extension for .js
Module._extensions['.js'] = function(module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);
};
module
对象的 _compile
在该函数中被调用,并且 这就是神奇发生的地方,
The module
object's _compile
is invoked in that function and this is where the magic happens,
// 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;
还有一些关于包装代码的事情,
And then there is something about wrapping the code,
// create wrapper function
var wrapper = Module.wrap(content);
我们着手寻找 Module.wrap
的作用,这不过是
We set out to find what Module.wrap
does, which is nothing but
Module.wrap = NativeModule.wrap;
在src/node.js
文件,这就是我们找到这个的地方,
which is defined in src/node.js
file and that is where we find this,
NativeModule.wrap = function(script) {
return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];
};
NativeModule.wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'
});'
];
这就是我们的程序访问魔术变量的方式,exports
、require
、module
、__filename
和 __dirname
This is how our programs have access to the magic variables, exports
, require
, module
, __filename
and __dirname
然后使用here编译并执行包装函数="https://nodejs.org/api/vm.html#vm_vm_runinthiscontext_code_options">runInThisContext
,
Then the wrapped function is compiled and executed here with runInThisContext
,
var compiledWrapper = runInThisContext(wrapper, { filename: filename });
最后,模块的编译包装函数对象被调用,如 this,为 exports
、require
, 模块
、__filename
和__dirname
And then finally, the module's compiled wrapped function object is invoked like this, with values populated for exports
, require
, module
, __filename
and __dirname
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
这就是 Node.js 处理和执行我们的模块的方式,这也是 return
语句正常工作的原因.
This is how our modules are processed and executed by Node.js and that is why the return
statement works without failing.
相关文章