Node.js中require的工作原理浅析


Posted in Javascript onJune 24, 2014

几乎所有的Node.js开发人员可以告诉你`require()`函数做什么,但我们又有多少人真正知道它是如何工作的?我们每天都使用它来加载库和模块,但它的行为,对于我们来说反而是一个谜。

出于好奇,我钻研了node的核心代码来找出在引擎下发生了什么事。但这并不是一个单一的功能,我在node的模块系统的找到了module.js。该文件包含一个令人惊讶的强大的且相对陌生的核心模块,控制每个文件的加载,编译和缓存。`require()`,它的横空出世,只是冰山的一角。

module.js

function Module(id, parent) {

  this.id = id;

  this.exports = {};

  this.parent = parent;

  // ...

在module.js在Node.js内部主要承担两个角色。首先,它为所有的Node.js模块提供了一个基础。每个文件是基本模块new出的一个新实例,即使在该文件已经运行之后,仍然存在。这就是为什么我们能够性为module.exports附加属并在需要时返回它们。

该模块的第二大任务是处理node的模块加载机制。我们使用的独立操作的“require”函数实际上是一个抽象概念的module.require,这本身就是只是一个简单的关于Module._load功能的封装。此load方法处理每个文件的实际加载,并在那里开始我们的旅程。

Module._load

Module._load = function(request, parent, isMain) {

  // 1. Check Module._cache for the cached module. 

  // 2. Create a new Module instance if cache is empty.

  // 3. Save it to the cache.

  // 4. Call module.load() with your the given filename.

  //    This will call module.compile() after reading the file contents.

  // 5. If there was an error loading/parsing the file, 

  //    delete the bad module from the cache

  // 6. return module.exports

};

Module._load负责加载新的模块和管理模块的缓存。缓存加载的每个模块减少冗余文件的读取次数,并可以显著地加快您应用程序的速度。此外,共享模块实例允许单例特性的模块,保持在项目中的状态。

如果某个模块没有在缓存中存在,Module._load将创建该文件的一个新的基本模块。然后,它会告诉模块在将它们发送到module._compile之前阅读新文件的内容。[1]

如果您注意到上面的步骤#6,你会看到module.exports已被返回给用户。这就是为什么当你在定义公共接口使用时,你使用exports和module.exports,因为Module._load将接下来返回require的内容。我很惊讶,这里没有更多的功能,但如果有的话那更好。

module._compile

Module.prototype._compile = function(content, filename) {

  // 1. Create the standalone require function that calls module.require.

  // 2. Attach other helper methods to require.

  // 3. Wraps the JS code in a function that provides our require,

  //    module, etc. variables locally to the module scope.

  // 4. Run that function

};

· 这是真正的奇迹发生的地方。首先,一个特殊的独立操作的require函数是为该模块创建的。这是我们需要的并且都熟悉的功能。而函数本身只是一个在Module.require的封装,它也包含了一些便于我们使用的鲜为人知的辅助方法:

· require():加载一个外部模块
· require.resolve():解析一个模块名到它的绝对路径
· require.main:主模块
· require.cache:所有缓存好的模块
· ·require.extensions:根据其扩展名,对于每个有效的文件类型可使用的编制方法

一旦require准备好了,整个加载的源代码就会被封装在一个新的函数里,可以接受require,module,exports和所有其他暴露的变量作为参数。这是一个仅仅为封装模块的而创建的函数,以便于在防止与Node.js的环境产生冲突。

(function (exports, require, module, __filename, __dirname) {

  // YOUR CODE INJECTED HERE!

});

该Module._compile方法是同步执行的,所以对Module._load的调用只能等到这段代码运行结束,并将module.exprts返回给用户。

结论

因此,我们已经了解了require的全部代码,并已经初步了解它是如何工作的。

如果你已经按照这一切的方式做了,那么你已经为最后的秘密做好准备:require('module')。这是正确的,该模块系统本身可以通过模块系统被加载。盗梦空间。这可能听起来很奇怪,但它可以让用户空间同模块加载系统互动起来,并不需要钻研Node.js核心。受欢迎的模块都像这样被建立。[2]

如果您想了解更多,请自己查看module.js源代码。还有很多东西足够你头痛一段时间了。第一个可以告诉我什么是NODE_MODULE_CONTEXTS“以及它为什么被添加的人可以得到加分奖励 :)

[1] module._compile方法只用于运行JavaScript文件。 JSON文件需通过JSON.parse() 解析并返回

[2]然而,这两个模块都建立在私有模块的方法,如Module._resolveLookupPaths和Module._findPath。你可以认为这并没有好多了...

Javascript 相关文章推荐
客户端静态页面玩分页
Jun 26 Javascript
利用try-catch判断变量是已声明未声明还是未赋值
Mar 12 Javascript
关于jQuery判断元素是否存在的问题示例探讨
Jul 21 Javascript
学习AngularJs:Directive指令用法(完整版)
Apr 26 Javascript
Javascript中级语法快速入手
Jul 30 Javascript
vue 2.0路由之路由嵌套示例详解
May 08 Javascript
react系列从零开始_简单谈谈react
Jul 06 Javascript
微信小程序之滚动视图容器的实现方法
Sep 26 Javascript
javascript中神奇的 Date对象小结
Oct 12 Javascript
微信小程序实现点击文字页面跳转功能【附源码下载】
Dec 12 Javascript
js实现表格数据搜索
Aug 09 Javascript
一篇文章带你搞懂Vue虚拟Dom与diff算法
Aug 25 Javascript
angularjs实现与服务器交互分享
Jun 24 #Javascript
完美兼容各大浏览器获取HTTP_REFERER方法总结
Jun 24 #Javascript
Javascript学习笔记之 函数篇(二) : this 的工作机制
Jun 24 #Javascript
Javascript学习笔记之 函数篇(一) : 函数声明和函数表达式
Jun 24 #Javascript
Javascript学习笔记之 对象篇(四) : for in 循环
Jun 24 #Javascript
Javascript学习笔记之 对象篇(三) : hasOwnProperty
Jun 24 #Javascript
js实现div闪烁原理及实现代码
Jun 24 #Javascript
You might like
php实现的漂亮分页方法
2014/04/17 PHP
PHP使用适合阅读的格式显示文件大小的方法
2015/03/05 PHP
php查询mysql数据库并将结果保存到数组的方法
2015/03/18 PHP
php版微信返回用户text输入的方法
2016/11/14 PHP
PHP+Mysql无刷新问答评论系统(源码)
2016/12/20 PHP
Yii框架使用PHPExcel导出Excel文件的方法分析【改进版】
2019/07/24 PHP
js获取指定日期前后的日期代码
2013/08/20 Javascript
Javascript实现检测客户端类型代码封包
2015/12/03 Javascript
浅谈EasyUI常用控件的禁用方法
2016/11/09 Javascript
JS日程管理插件FullCalendar简单实例
2017/02/07 Javascript
BootStrap表单时间选择器详解
2017/05/09 Javascript
微信小程序request出现400的问题解决办法
2017/05/23 Javascript
Angular+Bootstrap+Spring Boot实现分页功能实例代码
2017/07/21 Javascript
AngularJS中scope的绑定策略实例分析
2017/10/30 Javascript
vue如何进行动画的封装
2018/09/26 Javascript
解决微信小程序调用moveToLocation失效问题【超简单】
2019/04/12 Javascript
小程序登录/注册页面设计的实现代码
2019/05/24 Javascript
NodeJs实现简易WEB上传下载服务器
2019/08/10 NodeJs
微信小程序 下拉刷新及上拉加载原理解析
2019/11/06 Javascript
jquery使用echarts实现有向图可视化功能示例
2019/11/25 jQuery
js属性对象的hasOwnProperty方法的使用
2021/02/05 Javascript
Python之用户输入的实例
2018/06/22 Python
python中类的属性和方法介绍
2018/11/27 Python
python matplotlib如何给图中的点加标签
2019/11/14 Python
HTML5 通过Vedio标签实现视频循环播放的示例代码
2020/08/05 HTML / CSS
物业保安主管岗位职责
2013/12/25 职场文书
结婚典礼证婚词
2014/01/08 职场文书
致长跑运动员广播稿
2014/01/31 职场文书
音乐教学随笔感言
2014/02/19 职场文书
2014年两会学习心得体会
2014/03/10 职场文书
企业节能减排实施方案
2014/03/19 职场文书
工会工作个人总结
2015/03/03 职场文书
社区党建工作总结2015
2015/05/13 职场文书
关于元旦的广播稿2016
2015/12/17 职场文书
Oracle 多表查询基本语法实例
2022/04/18 Oracle
js前端面试常见浏览器缓存强缓存及协商缓存实例
2022/06/21 Javascript