深入理解node中require的原理及执行过程

前言


在朴灵老师的《深入浅出nodejs》一书中提到,每个模块文件的require,exports和module这3个变量并没有在模块中定义,也并非全局函数/对象。而是在编译的时候Node对js文件内容进行了头尾的包装。在头部加了(function (exports, require, module, __filename, __dirname) {,在尾部加了 \n});。这样看起来虽然貌似理解了require,exports和module的来源。但是依然不明白在这个函数外调用时,比如require时,发生了什么。于是花了点时间去仔细阅读了一下node的源代码,感觉豁然开朗,这里把我的分析过程记录一下,仅供大家参考。

源文件分析


有关require的内容,只要阅读两个文件就够了。分别是lib/internal/bootstrap_node.jslib/module.js

1、require入口: 

lib/module.js

可以看到,这里继续调用了_load方法用于加载文件

2、Module._load方法:

lib/module.js

可以看到,这里继续调用了_resolveFilename方法,顾名思义,这应该是一个解析需要require的文件名的方法

3、Module._resolveFilename方法:

lib/module.js

可以看到,这里又继续调用了_findPath方法

4、Module._findPath方法:

lib/module.js
lib/module.js

可以看到,这里完整的显示了node是如何根据require传入的名称来定位具体的文件的,他们的顺序依次是:

1、先从缓存中读取,如果没有则继续往下

2、判断需要模块路径是否以/结尾,如果不是,则要判断

    a. 检查是否是一个文件,如果是,则转换为真实路径

    b. 否则如果是一个目录,则调用tryPackage方法读取该目录下的package.json文件,把里面的main属性设置为filename

    c. 如果没有读到路径上的文件,则通过tryExtensions尝试在该路径后依次加上.js,.json和.node后缀,判断是否存在,若存在则返回加上后缀后的路径

3、如果依然不存在,则同样调用tryPackage方法读取该目录下的package.json文件,把里面的main属性设置为filename

4、如果依然不存在,则尝试在该路径后依次加上index.js,index.json和index.node,判断是否存在,若存在则返回拼接后的路径。

5、若解析成功,则把解析得到的文件名cache起来,下次require就不用再次解析了,否则若解析失败,则返回false

5、文件名成功获取后,再次回到Module._load方法:

lib/module.js

可以看到,这里继续调用了load方法来加载文件

6、Module.load方法:

lib/module.js

可以看到,这里将根据不同的文件类型(js,json和node),采用不同的加载方法

7、不同文件类型的加载方法不同:

lib/module.js

可以看到,js文件将在读入文件(同步读)内容后进行编译,json文件则用JSON.parse解析内容,node文件则使用dlopen进行动态链接库载入

8、这里仅针对通常的js类型的文件的载入进行分析:

lib/module.js

可以看到,js文件内容先被wrap(包装)了一下,然后才使用runInThisContext来运行包装后的代码,而传入的参数就是前面说的exports, require, module,还有当前文件名及所在目录名。此外,也看到,模块中的this其实是指向module的exports,而不是global!

9、Module.wrap:

可以看到,Module.wrap只是NativeModule.wrap的引用,这里的NativeModule则位于lib/internal/bootstrap_node.js中

10、NativeModule.wrap:

lib/internal/bootstrap_node.js

可以看到,这里就对上了文章开头所说的编译时node文件内容的头尾包装,自此,本次源码分析结果。

总结


从上面的分析可以看出来,exports其实是module的属性,require则是Module原型的方法。exports.xx=xx,其实跟module.exports.xx=xx其实是一样的,不过如果直接为export赋值,则不能写成exports=xx,而应该写成module.exports=xx,因为exports在这里只是一个引用。

从上面也可以看到,每一次require,都会把new一个Module,并且把这个Module添加到当前模块的children中,并且返回新建的Module对象的exports。

其实node启动的原理跟require是一样的,src/node.cc中的node::LoadEnvironment函数会被调用,在该函数内则会接着调用lib/internal/bootstrap_node.js中的代码,并执行startup函数,startup函数会执行Module.runMain方法,而Module.runMain方法会执行Module._load方法,参数就是命令行的第一个参数(比如: node ./app.js),如此,跟上面require就走到一起了。

lib/module.js

要想深入的理解一件事情的原理,还是需要仔细的阅读和研究底层的实现代码,好在node关于require实现原理方面的代码还挺简单的,想要深入理解node的同学还是很有必要仔细读一下的。

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

推荐阅读更多精彩内容