* * * * CommonJS norms
Rhino is a product of JavaScript’s quest for native programming that began shortly after Netscape. But then the way of server-side JavaScript are reference to many server-side languages to achieve, in such a background, one has no characteristics, two has no practical value. However, with the more and more extensive application of JavaScript in the front end and the promotion of JavaScript on the server side, the existing specification of JavaScript is very weak, which is not conducive to the large-scale application of JavaScript. In environments where JavaScript is the host language, there are only basic native objects and types, and more objects and apis are dependent on the host’s provision, so we can see that JavaScript lacks these features:
- JavaScript has no module system. There is no native support for closed scope or dependency management.
- JavaScript has no standard library. There are no file system apis, no IO stream apis, etc., except for some core libraries.
- JavaScript has no standard interface. There is no unified interface such as Web Server or database.
- JavaScript has no package management system. Dependencies cannot be automatically loaded and installed.
Hence the CommonJS (www.commonjs.org) specification, which aims to build a JavaScript ecosystem that includes Web servers, desktops, command-line tools, and browsers. CommonJS has specifications that address these issues, and Node.js is an implementation of these specifications. Node.js itself implements the require method as its method of introducing modules, and NPM also implements dependency management and automatic module installation based on the package specification defined by CommonJS. Here we’ll delve into node.js’ require mechanism and NPM’s package-based application.
Simple module definition and usage
In Node.js, it’s very convenient to define a module. We take two methods of calculating the area and circumference of a circle as examples to show how modules are defined in Node.js.
1 var PI = Math.PI;
2 exports.area = function (r) {
3 return PI * r * r;
4 };
5 exports.circumference = function (r) {
6 return 2 * PI * r;
7 };</pre>
}// Welcome to join the full stack development exchange circle to learn exchange: 582735936
]// For 1-3 years of front-end staff
} // Help break through technical bottlenecks and improve thinking ability
Copy the code
Save this file as circle.js, create a new app.js file, and write the following code:
1 var circle = require('./circle.js');
2 console.log( 'The area of a circle of radius 3 is ' + circle.area(
4)); </pre>Copy the code
As you can see, module calls are also convenient, requiring only the file that needs to be called.
After requiring this file, methods defined on exports objects can be called at will. Node.js encapsulates the definition and invocation of modules in a very simple and convenient way. Node.js’s module mechanism is excellent from the point of view that its API is user-friendly.
Module loading strategy
Node.js modules are divided into two categories, one is the native (core) module, one is the file module. Native modules are compiled into binary executables at the time node.js source code is compiled and load fastest. Another class of file modules are dynamically loaded and load slower than native modules. However, Node.js caches both native and file modules, so there is no repeat overhead when requiring a second time. Native modules are defined in the lib directory, while file modules are not.
node app.js
Copy the code
Almost all files started by command line loading are file modules. We’ll start with how Node.js loads file modules. Loading file modules is done primarily by the native module module, which is loaded at startup and directly calls the runMain static method.
1 // bootstrap main module.
2 Module.runMain = function () {
3 // Load the main module--the command line argument.
4 Module._load(process.argv[1].null.true); 5 };</pre>
Copy the code
The _load static method is executed after the file name is analyzed
var module = new Module(id, parent);
Copy the code
The current module object is cached according to the file path, and the module instance object is loaded according to the file name.
module.load(filename);
Copy the code
There are actually three different types of modules in the file module. These three types of file modules are distinguished by suffixes, and Node.js determines the loading method based on the suffix name.
- Js. Read JS file synchronously through FS module and compile execution.
- The node. Addon written by C/C++. Load through the dlopen method.
- Json. Read the file and call json.parse to parse the load.
Here we will describe the compilation process of the JS suffix in detail. The actual step that Node.js performs during the compilation of js files is to wrap the contents of js files.
In the case of app.js, the packaged app.js will be in the following form:
1 (function (exports, require, module, __filename, __dirname) {
2 var circle = require('./circle.js');
3 console.log('The area of a circle of radius 4 is ' + circle.area(4)); 4}); </pre>Copy the code
This code is executed through the VM native module’s runInThisContext method (similar to eval, but with explicit context, not global contamination) and returned as a concrete function object. Finally, the module object’s exports, require method, module, filename, directory name are passed as arguments and executed.
This is why require is not defined in the app.js file, but this method does exist. __filename, __dirname, module and exports are not defined but exist in node.js API documentation. __filename and __dirname are passed in after being analyzed during the search for the file path. The module variable is the module object itself, and exports is an empty object ({}, not null) initialized in the module constructor.
In this main file, the rest of the modules can be imported using the require method. And what this require method actually calls is load.
The load method returns the Module exports object after it has loaded, compiled, and cached the Module. This is why only methods defined on exports objects in the circe.js file can be called externally.
The module loading mechanism described above is defined in lib/module.js.