规范地址 http://www.commonjs.org/
nodejs modules文档地址 http://nodejs.cn/api/modules....
核心逻辑
在执行模块代码之前,nodejs会使用一个闭包(The module wrapper)封装起来,这就是它每一个文件都是一个独立的域的原因。但如果值没有用var声明的变量,会直接提升到全局上去,在其他文件也可以直接使用。
nodejs会通过下面一个闭包把文件给包起来
(function(exports, require, module, __filename, __dirname) {
// 模块的代码实际上在这里
});
我们可以使用
console.log(JSON.stringify(arguments))
可以看到当前js文件封装之后的参数
例:
aa.js
console.log(JSON.stringify(arguments))
// {"0":{},"2":{"id":".","exports":{},"parent":null,"filename":"D:\\code\\js\\aa.js","loaded":false,"children":[],"paths":["D:\\code\\js\\node_modules","D:\\code\\node_modules","D:\\node_modules"]},"3":"D:\\code\\js\\aa.js","4":"D:\\code\\js"}‘
可以看到这里返回的对象对应着exports, require, module, __filename, __dirname
exports
在整个域里,会有两个exports,一个模块封装器里的首个参数,一个是module类里的。
两个exports引用了同一块堆内存,require引用时实际上拿的是module类里的
require
用于引入模块、 JSON、或本地文件。 可以从 node_modules 引入模块。 可以使用相对路径(例如 ./、 ./foo、 ./bar/baz、 ../foo)引入本地模块或 JSON 文件,路径会根据 __dirname 定义的目录名或当前工作目录进行处理。
上面是官网对require的定义,require的一些属性使用在官网也有定义
module
在每个模块中, module 的自由变量是对表示当前模块的对象的引用。 为方便起见,还可以通过全局模块的 exports 访问 module.exports。 module 实际上不是全局的,而是每个模块本地的。
__filename
当前模块的文件名。 这是当前的模块文件的绝对路径(符号链接会被解析)。
__dirname
当前模块的目录名。 与 __filename 的 path.dirname() 相同。
特殊使用
循环引用
当循环调用 require() 时,一个模块可能在未完成执行时被返回。
例如以下情况:
a.js:
console.log('a 开始');
exports.dOne= false;
const b = require('./b.js');
console.log('在 a 中,b.dOne= %j', b.done);
exports.dOne= true;
console.log('a 结束');
b.js:
console.log('b 开始');
exports.dOne= false;
const a = require('./a.js');
console.log('在 b 中,a.dOne= %j', a.done);
exports.dOne= true;
console.log('b 结束');
main.js:
console.log('main 开始');
const a = require('./a.js');
const b = require('./b.js');
console.log('在 main 中,a.dOne=%j,b.dOne=%j', a.done, b.done);
当 main.js 加载 a.js 时, a.js 又加载 b.js。 此时, b.js 会尝试去加载 a.js。 为了防止无限的循环,会返回一个 a.js 的 exports 对象的 未完成的副本 给 b.js 模块。 然后 b.js 完成加载,并将 exports 对象提供给 a.js 模块。
当 main.js 加载这两个模块时,它们都已经完成加载。 因此,该程序的输出会是:
$ node main.js
main 开始
a 开始
b 开始
在 b 中,a.dOne= false
b 结束
在 a 中,b.dOne= true
a 结束
在 main 中,a.dOne=true,b.dOne=true