模块加载与运行原理


1.CommonJS规范


Node与浏览器以及W3C组织、CommonJS组织、ES之间的关系

(1)导出:exports和module.exports

    * exports:将要导出的对象作为其属性即可,只能导出实例化对象,若为特定的类型则会切断和module.exports的联系,因为exports是指向module.exports的一个引用

    * module.exportsrequire()返回的是module.exports导出的结果


若x是require该文件的结果变量,则x.a的结果是2

:exports -> {} <- module.exports,初始值是一个空对象;

(2)导入:require()

* 优先级:缓存加载(node缓存的是编译和执行后的对象,浏览器缓存的是文件) —》核心模块加载—》文件模块加载;

* 导入步骤:路径分析—》文件定位—》编译执行

2.Node运行原理

(1)当输入node xx.js执行.js文件时干了什么?

* 调用node项目的src/node_main.cc入口文件:区分运行环境是windows环境和*nix环境,调用node::start()方法进入src/node.cc文件中;


以UNIX环境为例的main()函数

* 进入src/node.cc文件中:Start() —》StartNodeInstance() —》LoadEnvironment():将bootstrap_node.js文件封装在一个function中,通过f->call()进入bootstrap_node.js文件实现xx.js的加载、编译和执行;

引入并调用bootStrap_node.js文件
process.arg[1]取出文件名xx.js->path.resolve()解析文件路径->run(Module.runMain)编译执行xx.js

综上:node xx.js启动一个文件就是require(xx.js)

3.模块加载原理

(1)模块实例:文本模块核心模块的模块实例区别:

文本模块实例的构造函数
js核心模块实例的构造函数

(1)require()源码在lib/module.js中:

Module的原型链上,每个模块实例都有一个require()方法,其内部调用Module._load()方法

(2)Module._load():require()实现的原理:确定绝对路径(Module._resolveFilename())+加载(根据返回的模块标识符:优先缓存->NativeModule.require()/tryModuleLoad())

Module._cache()将new的模块实例缓存

* Module._resolveFilename():确定模块的绝对路径,以它作为模块的标识符;

举例说明Module._findPath()过程如下:

 * 模块加载:分核心模块和文本模块:

1)核心模块:NativeModule.require()加载;

    js核心模块process.binding('natives')从内存中读取,再编译执行;

    c/c++核心模块:process.binding(内建模块名)通过get_builtin_module()从内存中读取直接执行;

 2)文件模块:module.load():node会新建一个模块对象,然后根据绝对路径载入并编译执行,将结果缓存;

针对不同文件模块有不同的载入方法


js文件模块:通过fs.readFileSync()载入


JSON文件模块:通过JSON.parse()载入


.node文件模块:通过process.dlopen()得到编译后的可执行文件

4.模块的编译执行:调用具体的编译方式将文件执行后返回给调用者

(1)核心模块:在node项目创建时通过node.gyp将所有的c/c++文件编译为二进制文件存储在node内存中(这不属于require()时的操作了);js核心模块在require()时还需要编译一次,而c/c++核心模块就不需要了;

* js核心模块

1)node项目创建时的编译—转为c/c++代码:采用V8的js2c.py库将所有的js模块文件代码以字符串形式存储在c++的数组中,生成node.natives.h头文件;启动node进程时,js模块文件代码以字符串形式直接加载进内存中

2)NativeModule.require():通过模块id做索引从natives数组中取出js核心模块的源码,再通过wrapper()头尾包装(),执行和导出exports对象,将其编译结果缓存到NativeModule._cache对象上;

* c/c++核心模块:编译过程在node项目创建时(生成node.exe),通过node_module_struct结构体定义到node命名空间(就知道了有哪些内建模块了),在.h头文件里将内建模块统一放进node_module_list的数组中,然后被编译进二进制文件加载进node内存中;

(2)文本模块:只有.js文件需要编译;

      .js文件:其过程和js核心模块第二次编译几乎一样;

      .node文件:是c/c++扩展文件编译后的结果:与c/c++核心模块编译类似(通过node-gyp),只是不需要写入node命名空间,在require()前都已经编译好了,加载之后不需要编译了,直接执行之后就可以被外部调用了;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 我想试试养一些多肉植物,恳求妈妈买来一些肉苗,终于可以开始种啦!品种好多有些眼花缭乱啦。 我在花盆底部垫上干苔藓,...
    张榕柏燚阅读 414评论 5 2