Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a module level return statement work in 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"); 

This works without any errors and prints:

Trying to reach 

in the standard output but not "dead code" - the return actually ceased execution.

But according to the specification of return statements in ECMAScript 5.1,

Semantics

An ECMAScript program is considered syntactically incorrect if it contains a return statement that is not within a FunctionBody.

In the program shown above return is not within any function.

Then why doesn't this throw?

like image 689
thefourtheye Avatar asked Mar 10 '15 02:03

thefourtheye


People also ask

What is the purpose of a return statement in a function in JavaScript?

The return statement ends function execution and specifies a value to be returned to the function caller.

What are node JS modules and what is their purpose?

In Node. js, Modules are the blocks of encapsulated code that communicates with an external application on the basis of their related functionality. Modules can be a single file or a collection of multiples files/folders.

Which module is used for reading and writing files in node JS?

js provides the functionality to read and write files from the computer. Reading and Writing the file in Node. js is done by using one of the coolest Node. js modules called fs module, it is one of the most well-known built-in Node.

How do nodes work in JavaScript?

Node. js is an event loop single-threaded language. It can handle concurrent requests with a single thread without blocking it for one request. Non-blocking I/o: Non-blocking i/o means working with multiple requests without blocking the thread for a single request.


1 Answers

TL;DR

The modules are wrapped by Node.js within a function, like this:

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

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"); }); 

That is why the program prints only Trying to reach and skips the console.log following the return statement.

Internals

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.

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(); }; 

In the Module._load function, a new Module object is created and it is loaded.

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

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; }; 

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); }; 

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. 

This is where the require function, used by our node modules's is created first.

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); 

We set out to find what Module.wrap does, which is nothing but

Module.wrap = NativeModule.wrap; 

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) { ',   '\n});' ]; 

This is how our programs have access to the magic variables, exports, require, module, __filename and __dirname

Then the wrapped function is compiled and executed here with runInThisContext,

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

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); 

This is how our modules are processed and executed by Node.js and that is why the return statement works without failing.

like image 112
thefourtheye Avatar answered Sep 16 '22 16:09

thefourtheye