"Turn" COMMONJS specification and Node module implementation

Source: Internet
Author: User
Tags script tag

node in the implementation is not exactly in accordance with the COMMONJS specification implementation, but the module specification has a certain trade-offs, but also added a little bit of the characteristics of their own needs. This article will introduce the module implementation of Nodejs in detail

Introduced

Nodejs is different from JavaScript, the top-level object in JavaScript is window, and the top-level object in node is the global

[note] In fact, JavaScript also exists as a global object, except that it is not accessed externally and uses the Window object to point to the global object.

In JavaScript, through var a = 100, you can get 100 by WINDOW.A.

But in the Nodejs, can not be accessed through GLOBAL.A, get is undefined

This is because var a = 100; the variable A in this statement is just the variable a in the module range, not a under the global object

In Nodejs, a file is a module, and each module has its own scope. A variable declared with Var, which is not global but belongs to the current module

If you want to declare a variable under a global scope, as follows

Overview

The modules in node are divided into two categories: one is node-provided modules, called core modules, and the other is a user-written module called a file module.

The core module is compiled into a binary execution file during the compilation of node source code. When the node process starts, some of the core modules are loaded directly into memory, so when this part of the core module is introduced, the two steps of file location and compile execution can be omitted and prioritized in the path analysis, so it is loaded faster

The file module is dynamically loaded at runtime and requires complete path analysis, file location, and compilation execution, which is slower than the core module.

Next, we expand the detailed module loading process

Module loading

In JavaScript, the load module uses the script tag, and in Nodejs, how do you load another module in one module?

Use the Require () method to introduce

"Cache Load"

Before expanding the analysis of identifiers that introduce the require () method, you need to know that, like the front-end browser caches static script files to improve performance, node caches the introduced modules to reduce the overhead of two ingestion. The difference is that the browser caches only the files, and node caches the objects after compilation and execution.

Whether it is the core module or the file module, the Require () method uses cache precedence for two loads of the same module, which is the first priority. The difference is that the cache check of the core module is preceded by the cache check of the file module

"Identifier Analysis"

The Require () method takes an identifier as an argument. In the node implementation, it is based on such an identifier for the module lookup. module identifiers are divided into the following categories in node: [1] core modules, such as HTTP, FS, path, and so on; [2]. Or. Starting relative path file module; [3] Absolute path file module with/starting; [4] Non-path form of file module, such as Custom connect module

According to the different format of the parameters, the require command goes to different paths to find the module file

1. If the argument string starts with "/", it means that a module file is loaded in the absolute path. For example, require(‘/home/marco/foo.js‘) the load/home/marco/foo.js

  2、If the argument string begins with "./", it means that a module file is loaded in a relative path (as opposed to the location of the current execution script). For example, the require(‘./circle‘) current script will load the same directory as thecircle.js

3. If the parameter string does not start with "./" or "/", it means that a default supplied core module (located in the system installation directory of node) is loaded, or an installed module (global or partial) at all levels of the Node_modules directory.

[note] If it is a file module under the current path, be sure to start with a./, otherwise Nodejs will attempt to load the core module, or the module inside the Node_modules

A.jsconsole.log (' AAA ');//b.jsrequire ('./a ');//' AAA ' require (' a ');//Error

"File extension Analysis"

Require () during parsing of identifiers, there is a case where the file name extension is not included in the identifier. The COMMONJS module specification also allows a file extension to be included in the identifier, in which case the node will first look for the file without a suffix, and if not, press the. js,. JSON,. Node order to make up the extension, and then try

During the attempt, the FS module needs to be called synchronously to determine if the file exists. Because node is single-threaded, this is a place that can cause performance problems. The trick is: if you have a. node and. json file, a little speed can be speeded up with the extension in the identifier passed to require (). Another trick is to synchronize with the cache, which can significantly alleviate the pitfalls of blocking calls in node single-threaded

"Directory Analysis and Packages"

During the parsing of identifiers, require () may not find the corresponding file after parsing the file name extension, but it gets a directory, which is often present when a custom module is introduced and a module-by-block path is found, and node handles the directory as a package

In this process, node has a certain degree of support for the COMMONJS package specification. First, node looks in the current directory for Package.json (package description file COMMONJS Package specification definition), resolves the package description object by Json.parse (), and takes the filename specified by the main property to locate. If the file name is missing an extension, you will enter the extension analysis step

If the main property specifies a filename error, or there is no Package.json file at all, node will use index as the default file name, and then find Index.js, Index.json, Index.node

If no files are successfully located during directory analysis, the custom module goes to the next module path to find it. If the module path array is traversed and the target file is still not found, the lookup failed exception is thrown

accessing variables

How do I access variables defined in another module in one module?

"Global"

The easiest way to do this is to copy a module-defined variable to the global environment, and then another module to access the global environment to

A.jsvar a = 100;GLOBAL.A = A;//b.jsrequire ('./a '); Console.log (GLOBAL.A);//100

Although this method is simple, it is not recommended because it pollutes the global environment.

"Module"

The common approach is to use the Module object modules provided by Nodejs, which holds some information about the current module

function Module (ID, parent) {    this.id = ID;    This.exports = {};    This.parent = parent;    if (parent && parent.children) {        Parent.children.push (this);    }    This.filename = null;    this.loaded = false;    This.children = [];}
The identifier for the Module.id module, usually a module file name with an absolute path. The file name of the Module.filename module with an absolute path. Module.loaded returns a Boolean value that indicates whether the module has finished loading. Module.parent returns an object that represents the module that called the module. Module.children returns an array that represents the other modules to be used by the module. The module.exports represents the value of the module's external output.

"Exports"

  module.exportsproperty represents the external output interface of the current module, and the other file loads the module, actually reading the module.exports variable

A.jsvar a = 100;MODULE.EXPORTS.A = A;//b.jsvar result = require ('./a '); Console.log (result);//' {a:100} '

For convenience, node provides a exports variable for each module, pointing to Module.exports. The result is that you can add methods to the exports object when the external output module interface

Console.log (Module.exports = = = exports);//true

[note] It is not possible to direct the exports variable to a value because it is equivalent to cutting off the exports module.exports contact

Module compilation

Compilation and execution are the last stages of a module implementation. When you navigate to a specific file, node creates a new module object and then loads and compiles it according to the path. The loading method differs for different file name extensions, as shown below

JS file--Compile execution after reading files synchronously through FS module

Node file--This is an extension file written in C + +, and the last compiled generated file is loaded via the Dlopen () method

JSON file--After reading the file synchronously through the FS module, using Json.parse () to parse the returned result

The rest of the extension files--they are all loaded as. js files

Each successfully compiled module caches its file path as an index on the Module._cache object to increase the performance of the two introduced

Depending on the file extension, node invokes a different read method, such as a. json file, called the following:

Native extension for. jsonmodule._extensions['. JSON '] = function (module, filename) {    var content = Nativemodule.re Quire (' FS '). Readfilesync (filename, ' utf8 ');     try {        module.exports = json.parse (Stripbom (content)),    } catch (Err) {        err.message = filename + ': ' + err.mess Age;        throw err;    }};

Where Module._extensions is assigned to the extensions attribute of require (), so by accessing Require.extensions in the code, you can see how the extended loading method already exists in the system. Write the following code to test:

Console.log (require.extensions);

The results of the implementation are as follows:

{'. js ': [function], '. JSON ': [function], '. Node ': [function]}

After determining the file's extension, node invokes the specific compilation method to return the file to the caller

"Compilation of JavaScript modules"

Back to the COMMONJS module specification, we know that there are 3 variables in each module file, such as require, exports, module, but they are not defined in the module file, so where do they come from? Even in node's API documentation, we know that there are two variables, filename, dirname, in each module, and where did they come from? If we put the process of directly defining the module on the browser side, there will be a situation where the global variables are polluted.

In fact, during compilation, node wraps the contents of the JavaScript file that was acquired. Added in the header (function (exports, require, module, filename, dirname) {\ n, added \ n} at the tail);

A normal JavaScript file will be packaged like this

(function (exports, require, module,  filename,  dirname) {    var math = require (' math ');    Exports.area = function (RADIUS) {        return Math.PI * radius * RADIUS;    };});

This allows for scope isolation between each module file. The code after the wrapper is executed by the VM native module's Runinthiscontext () method (similar to eval, which has a clear context, does not pollute the global), and returns a specific function object. Finally, the exports property of the current module object, the Require () method, the module (Modules object itself), and the full file path and file directory obtained in the file location are passed as parameters to the function () execution

This is why these variables are not defined in each module file. After execution, the module's exports property is returned to the caller. Any methods and properties on the exports property can be called externally, but the rest of the variables or properties in the module cannot be called directly

At this point, require, exports, module of the process is complete, this is node to the COMMONJS module specification implementation

"Compilation of C + + modules"

Node calls the Process.dlopen () method to load and execute. Under node's architecture, the Dlopen () method has different implementations under the Windows and *nix platforms, encapsulated by the LIBUV compatibility layer

In fact,. node's module file does not need to be compiled, because it is compiled and generated after the C + + module is written, so there is only the process of loading and executing. During execution, the module's exports object is associated with the. Node module and then returned to the caller

The advantages of the C + + module for node users are mainly the implementation of efficiency, the disadvantage is that the C + + module writing threshold is higher than javascript

"Compilation of JSON Files"

The compilation of. json files is the simplest of the 3 ways to compile. Node uses the FS module to synchronously read the contents of the JSON file, calls the Json.parse () method to get the object, and assigns it to the exports of the module object for external invocation

JSON files are useful when used as a configuration file for a project. If you define a JSON file as a configuration, you do not have to call the FS module to read and parse asynchronously, call require () directly. In addition, you can enjoy the convenience of the module cache, and there is no performance impact when introduced two times

CommonJS

After the introduction of the module implementation of node, back to the end to learn the COMMONJS specification, relatively easy to understand

The COMMONJS specification is designed to compensate for current JavaScript's lack of standards and to have the basic ability to develop large-scale applications, rather than being stuck in a small scripting phase

Commonjs the definition of modules is very simple, mainly divided into module reference, module definition and module identification 3 parts

"Module Reference"

var math = require (' math ');

In the COMMONJS specification, there is the require () method, which accepts the module identifier as a way to introduce a module's API into the current context

"Module definition"

In the module, the context provides the require () method to introduce an external module. Corresponding to the introduced functionality, the context provides a method or variable that the exports object uses to export the current module, and it is the only exported export. In the module, there is also a Module object, which represents the module itself, and exports is the property of the modules. In node, a file is a module that mounts the method as a property on the exports object to define how the export will be exported:

Math.jsexports.add = function () {    var sum = 0, i = 0,args = arguments, L = args.length;    while (I < l) {        sum + = args[i++];    }    return sum;};

In another file, after we introduce the module through the Require () method, we can invoke the defined property or method.

Program.jsvar math = require (' math '), exports.increment = function (val) {    return Math.add (Val, 1);};

"Module Identification"

The module identifier is actually a parameter passed to the Require () method, which must be a string that conforms to the small hump name, or a. 、.. The relative path to the beginning, or the absolute path. It can have no filename suffix. js

The definition of the module is very simple and the interface is very concise. Its significance is to limit the clustering methods and variables to the private scope, while supporting the introduction and export capabilities to seamlessly connect upstream and downstream dependencies. Each module has a separate space, they do not interfere with each other, it also looks neat when referenced

Source Address: http://www.cnblogs.com/xiaohuochai/archive/2017/05/13/6847939.html

"Turn" COMMONJS specification and Node module implementation

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.