Module classification
Node.js has two kinds of modules
- Core module
Part of the core modules have been directly loaded into the memory, the steps of path analysis and compilation can be omitted and they are judged first in the path analysis, so the loading speed is the fastest - File module
Runtime dynamically loaded , so it is necessary the full file path analysis to locate and compile and run process, so speed is slower than the Core Module
The secret of implementing the "module" function is that JavaScript is a functional programming language that supports closures. If we wrap a piece of JavaScript code with a function, all "global" variables of this code become local variables inside the function.
var s = 'Hello';
var name = 'world';
console.log(s + ' ' + name + '!');
(function() {
var s = 'Hello';
var name = 'world';
console.log(s + ' ' + name + '!');
})()
In this way, the original global variable s is now a local variable inside the anonymous function. If Node.js continues to load other modules, the "global" variables defined in these modules will not interfere with each other.
Therefore, Node uses JavaScript's functional programming feature to easily realize module isolation.
Module cache mechanism
Nodejs will cache the loaded modules to reduce the overhead during the second introduction. When the module is introduced, it will look up from the cache first. Node caches the objects after compilation and execution
Cache form: In the form of key-value, the real path is used as the key, and the result of the compilation and execution is used as the value and placed in the cache (in the Module._cache object) (the second load is faster)
Print rquire.cache
to see the cached objects
Circular references of modules
Let me talk about the conclusion first, because Node.js caches loaded modules, the circular dependencies of all modules will not cause infinite circular references. for example:
a.js
file
console.log('a starting');
exports.done = false
const b = require('./b.js')
console.log('in a, b done = %j', b.done);
exports.done = true
console.log('a done');
b.js
file
console.log('b starting');
exports.done = false
// 这里导入的是a未执行完的副本
const a = require('./a.js')
console.log('in b, a done = %j', a.done);
exports.done = true
console.log('b done');
main.js
file
console.log('main starting');
const a = require('./a')
const b = require('./b')
console.log('in main.js, a done = %j, b done = %j', a.done, b.done);
The entire detailed process analysis is as follows:
- node main.js
- require a.js, load a.js, output "a starting"
- a: exports.done = false,require b.js,load b.js
- Output "b starting", b: exports.done = false
- require a.js, because a.js has not been executed, the unfinished copy to , so a = {done: false}
- Output in b, a.done = false
- b: exports.done = true, output b done, b.js is executed, return to a.js to continue execution
- b = {done: true }, output in a, b.done = true, output a done
- After a.js is executed, a = {done: true}, return to main.js to continue execution, require b.js
- Since b.js has been executed and the value is in the cache, now a = {done: true }, b = {done: true}
- Output in main, a.done = true, b.done = true
It can be seen that Node.js caches the loaded modules, which solves the problem of circular references, and directly fetches them from the cache during the second load, which improves the loading speed.
Path analysis and file location
We need to understand that custom modules are dynamically loaded (loaded at runtime). When they are loaded for the first time, they have to go through the process of path analysis, file positioning, and compilation and execution.
When analyzing the path module, the require()
method will find the real path
- If there is no extension, it will be matched in sequence: .js> .node> .json
- If the corresponding file is not found, but a directory is obtained, it will be treated as a package
First searchpackage.json
inmain
attribute specifies the file name to locate> index.js> index.node> index.json order matching
Compilation of modules
Compilation and execution is the last stage of introducing file modules. Here I only talk about the compilation .js
fs
module, the compilation is performed. Each successfully compiled module will be cached on the Module._cache
object with its real path as an index to improve the performance of the second introduction.
During the compilation process, Node will wrap the obtained files head and tail
(function(module, exports, require, __filename, __dirname) {
})
In this way, the scope is isolated also explains why we can use these variables module、exports、__filename、 __dirname
The difference between module.exports and exports
When Node.js executes a javascript
file, it will generate a module
and exports
object, module
also has a exports
attribute, module.exports
and exports
point to the same reference
The fundamental difference between the two is:
exports returns the module function, module.exports returns the module object itself
for example:a.js
file
let sayHello = function() {
console.log('hello');
}
exports.sayHello = sayHello
b.js
file
// 这样使用会报错
const sayHello = require('./a')
sayHi()
// 正确的方式
const func = require('./a')
func.sayHello() // hello
Create a new file c.js
let sayHello = function() {
console.log('hello');
}
// 1方式导出
module.exports.sayHello = sayHello
// 2方式导出
module.exports = sayHello
Introduced in b.js
// 1方式的
const func = require('./a')
func.sayHello() // hello
// 2方式的
const sayHello = require('./a')
sayHello() // hello
It can be seen that the export of method 1 is the same as the export of exports in the same way at the time of introduction.
module.exports.sayHello = sayHello
等同于
module.exports = {
sayHello: sayHello
}
也等同于
exports.sayHello = sayHello
Another point to note is that when the require()
method is executed, the content exported by module.exports
// d.js文件下:
exports = {
a: 200
}
module.exports = {
a: 100
}
// b.js引入
const value = require('./d')
console.log('value', value); // {a: 100}
It can be seen from the above that, in fact, the content exported by require is the content of the memory block pointed to by module.exports, not exports.
// 如果d.js文件变成
exports = {
a: 200
}
// b.js引入
const value = require('./d')
console.log('value', value); // {}
You can see the printed value {}
it is because exports
original point with module.exports
a reference to the same, now exports = {a: 200}
Exports points to another memory address, to break off relations with module.exports, default module.eports={}
Summarize
- exports is a reference to module.exports
- The initialization of module.exports is {}, and exports are also {}
- The require reference returns module.exports, not exports
- exports.xxx = xxxx is equivalent to directly adding attributes or modifying attribute values on the exported object, which is directly visible in the calling module
- exports = xxx Reallocate memory for exports, which will be separated from module.exports. The two are unrelated. The calling module will not be accessible.
refer to:
Node module mechanism does not fully refer to the north
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。