内置模块
CommonJS 规范
模块的定义
上下文提供了 exports 对象用于导出当前模块的方法或者变量,并且它是位移导出的出口。
在模块中还存在一个 module 对象,它代表模块自身,而 exports 是 module 的属性。一个文件就是一个模块,将方法挂载在 exports 对象上作为属性即可定义导出的方式。
模块的实现
分三个步骤
- 路径分析
- 文件定位
- 编译执行
Node 提供的模块叫做核心模块,用户编写的模块叫做文件模块。
核心模块在 Node 源代码编译的过程中,编译进了二进制执行文件,在 Node 进程启动的时候部分核心模块就被直接加载进内存中,所以在这部分核心模块引入的时候,文件定位和编译执行这两个阶段可以省略,并且在路径分析中优先判断,加载速度最快。
- 优先从缓存加载
- 路径 分析和文件定位
文件定位 文件年扩展名分析
模块编译
在 Node 中,每个文件模块都是一个对象,定义如下
function Module(id, parent) {
this.id = id;
this.exports = {};
this.parent = parent;
if (parent && parent.children) {
parent.children.push(this);
}
this.filename = null;
this.loaded = false;
this.children = [];
}
每一个编译成功的模块都会将文件路径作为索引存在 Module._cache 对象上
Module._extensions['json'] = function(module, filename) {
var content = NativeModule.require('fs').readFielSync(filename, 'utf8');
try {
module.exports = JSON.parse(stripBOM(content));
} catch(err) {
err.message = filename + ':' + err.message;
throw err;
}
}
核心模块
编译过程
将编译所有 CC++文件之前,将所有 js 模块文件编译为CC++代码。
- 转成为C/C++代码
Node 采用 V8 附带的 js2c.py 工具,将所有内置的 Javascript 代码,转换成 C++ 里的数组,生成 node_natives.h 头文件。
js 代码以字符串的形式存储在 node 命名空间中,是不可执行的。在启动 Node 进程时,js 代码直接加载进内存中,核心模块经历标识符分析后直接定位到内存中,比普通的文件模块从磁盘中一处一处查找要快的多。
- 编译核心模块
纯CC++编写的部分称为内建模块,通常不被用户直接调用。Node 的 buffer、crypto、evals、fs、os 等模块都是部分通过 CC++ 编写的。
内建模块的结构定义
struct node_module_struct {
int version;
void *dso_handle;
const char *filename;
void (*register_fun) (v8::Handle<v8::Object> target);
const char *modname;
}