乐趣区

CommonJS浅析

规范地址 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


退出移动版