JavaScript核心模块的编译过程;
在编译所有C/C++
文件之前,编译程序需要将所有的JavaScript
模块文件编译为C/C++
代码,此时编译完成的代码还不可执行。
1. 转存为C/C++代码
Node采用了V8
附带的js2c.py工具,将所有内置的JavaScript
代码(src/node.js
和lib/*.js
)转换成C++
里的数组,生成node_natives.h
文件。
在这个过程中,JavaScript
代码以字符串的形式存储在node命名空间中,是不可直接执行的。
2. 编译JavaScript核心模块
lib
目录下的所有模块文件也没有定义require、module、exports
这些变量,在引入JavaScript
核心模块的过程中,也经历了头尾包装的过程,然后执行和导出了export
对象。
与文件模块的区别在于获取源代码的方式(
核心模块是从内存中加载的
)以及缓存执行结果的位置。
JavaScript
核心模块定义如下所示:
function NativeModule(id){
this.filename = id + '.js';
this.id = id;
this.exports = {};
this.loaded = false;
}
NativeModule._source = process.binding('native');
NativeModule._cache = {};
源文件通过
process.binding('native')
取出,编译成功的模块缓存到NativeModule._cache
对象上,文件模块则缓存到Module._cache
对象上。
C/C++核心模块的编译过程
在核心模块中,有些模块全部由C/C++
编写,有些模块则由C/C++
完成核心部分,其他由JavaScript
实现包装或向外导出,以满足性能需求。
C++
模块主内完成核心,JavaScript
主外实现封装的模式是node能够提高性能的常见方式。
纯C/C++
编写的部分统一称为内建模块,通常不能被用户直接调用。比如buffer、crypto、evals、fs、os
都是部分由C/C++
编写的。
2. 内建模块的优势
- 它们本身由
C/C++
编写,性能上优于脚本语言;- 在进行文件编译时,它们被编译进二进制文件,一旦
Node
开始执行,它们被直接加载进内存中,无需再次做标识符定位。
3. 模块之间的依赖
Node的所有模块类型中,存在一种依赖层级关系,文件模块可能会依赖核心模块,核心模块可能会依赖内建模块。
通常不推荐直接调用内建模块,如果需要,直接调用核心模块即可,因为核心模块中基本都封装了内建模块。
4. 内建模块的导出
Node在启动时,会生成一个全局变量process
,并提供Binding()
方法来协助加载内建模块。(Binding()的实现代码在src/node.cc中
)。
加载内建模块时,先创建一个
exports
空对象,然后调用get_builtin_module()
方法去除内建模块对象,通过register_func()
填充exports
对象,最后exports
对象按模块名缓存,并返回给调用方完成导出。
核心模块的引入流程
从下图可以看出核心模块的引入流程,它要经历C/C++层面的内建模块定义、(JavaScript
)核心模块的自定义和引入以及(JavaScript
)文件模块层面引入。
网友评论