1.在CommonJs规范中:
- 一个文件就是一个模块,拥有单独的作用域;
- 普通方式定义的变量、函数、对象都属于该模块内;
- 通过require来加载模块;
- 通过exports和modul.exports来暴露模块中的内容;
2.模块加载机制
- require(同步加载)基本功能:读取并执行一个JS文件,然后返回该模块的exports对象,如果没有发现指定模块会报错;
- 核心模块(位于Node的系统安装目录中)。
- 如果是一个目录,那么require会先查看该目录的package.json文件,然后加载main字段指定的脚本文件。否则取不到main字段,则会加载index文件如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js文件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会议编译后二进制文件解析。
- 如果不是核心模块也不是一个目录
- 当前文件目录下的node_modules目录下;
- 没有符合条件的则去当前目录的父文件夹的node_modules目录下;
- 若没有则再往上一层目录的node_modules目录下;
- 若没有则重复3,直到找到符合的模块或者根目录为止。
- 如果想得到require命令加载的确切文件名,使用require.resolve()方法。
3.模块的循环加载
- 模块可以多次加载,但只会在第一次加载的时候运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果;模块的加载顺序,按照代码的出现顺序是同步加载的; 如果发生模块的循环加载,即A加载B,B又加载A,则B将加载A的不完整版本。
// a.js
exports.x = 'a1';
console.log('a.js ', require('./b.js').x);
exports.x = 'a2';
// b.js
exports.x = 'b1';
console.log('b.js ', require('./a.js').x);
exports.x = 'b2';
// main.js
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);
上面代码是三个JavaScript文件。其中,a.js加载了b.js,而b.js又加载a.js。这时,Node返回a.js的不完整版本,所以执行结果如下。
$ node main.js
b.js a1
a.js b2
main.js a2
main.js b2
修改main.js,再次加载a.js和b.js。
// main.js
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);
console.log('main.js ', require('./a.js').x);
console.log('main.js ', require('./b.js').x);
执行上面代码,结果如下。
$ node main.js
b.js a1
a.js b2
main.js a2
main.js b2
main.js a2
main.js b2
- 上面代码中,第二次加载a.js和b.js时,会直接从缓存读取exports属性,所以a.js和b.js内部的console.log语句都不会执行了。
4.模块封装器
(function(exports, require, module, __filename, __dirname) {
// 模块的代码实际上在这里
});
5.模块作用域
__dirname:当前模块的目录名__filename:当前模块的文件名。 这是当前的模块文件的绝对路径(符号链接会被解析)。exports:module:require(id):
6.require对象
require.cache 被引入的模块将被缓存在这个对象中
require.main Module 对象,表示当 Node.js 进程启动时加载的入口脚本
require.resolve(request [,options])
使用内部的 require() 机制查询模块的位置,此操作只返回解析后的文件名,不会加载该模块
require.resolve.paths(request)
7.module 对象
-
每个模块都有一个module变量,该变量指向当前模块。module不是全局变量,而是每个模块都有的本地变量。所有代码都运行在模块作用域,不会污染全局作用域;
-
module.id模块的识别符,通常是带有绝对路径的模块文件名。 -
module.filename模块的文件名。 -
module.loaded返回一个布尔值,表示模块是否已经完成加载。 -
module.parent返回一个对象,表示调用该模块的模块。 -
module.children返回一个数组,表示该模块要用到的其他模块。 -
module.exports和exports:为了方便,node为每个模块提供一个exports变量,其指向module.exports,相当于在模块头部加了这句话:var exports = module.exports,在对外输出时,可以给exports对象添加方法,PS:不能直接赋值(因为这样就切断了exports和module.exports的联系); -
module.paths:模块的搜索路径。 -
module.require(id):module.require() 方法提供了一种加载模块的方法,就像从原始模块调用 require() 一样。