nodejs导出模块功能原理的个人理解

假设我们创建了一个模块foo.js,内容如下(这个模块的功能只是的导出一个简单对象):

var obj = {name: 'bar'};
module.exports = obj;

一般会这样使用这个模块:

var foo = require('./foo.js');
console.log(foo.name);/*输出 bar*/

我们给require()传入一了个模块文件,然后就从返回值中得到了文件中导出的对象。nodejs内部的处理原理大概可以简单描述

1.声明一个模块加载构造函数,它至少包括一个名为exports的属性,值为一个空对象:

function ModuleP(){
this.exports = {};
...
}

2.如果是初次加载js模块文件,在node定位到具体js文件后,就先创建一个Module的实例:

var moduleP = new ModuleP;

3.node通过内置fs模块同步读取文件(这里读取文件时必然会导致I/O阻塞):

var fs = require('fs');
var foo_js = fs.readFileSync('./foo.js');

4.读取完成后,node会对读取的文件内容进行头尾包装(wrapper)。大概就是通过字符串拼接,把读取到的文件代码都包装到一个匿名函数中:

var packStr = '(function(exports, module){'+ foo_js + ' return module.exports;})';
/*这时它只是一个字符串*/

5.把包装的代码传入一个类似eval的函数执行,返回一个匿名的function对象,原来foo.js文件中的代码都被写到了这个function中,作用域就自然被封装隔离起来了,这时它就是一个真正意义的模块了:

var packObj = eval(packStr);
/*通过eval函数,把wrapper后的字符串变成了一个匿名函数,并返回、赋值给变量packObj*/

6.把moduleP对象的exports属性和moduleP对象本身作为参数传入上一步创建的function执行,这时在第四步的包装过程中给匿名函数最后加入的代码 "return module.exports;"就发挥出模块导出的作用了:

var foo = packObj(moduleP.exports, moduleP);
/*这里就得到了从模块中导出的变量*/


当然,node的具体实现肯定比这个要复杂的多,会考虑到模块路径查找、模块缓存、文件读取时去掉bom信息等等。

但从这里可以知道这么几点:

1.模块文件中的代码被node加载完会立即被处理一次,封装成模块后执行。

2.node模块文件中之所以能用直接使用exports、module这两个对象,是因为它们其实是通过参数传递过来的,都是形参名。exports的实参是moduleP.exports,module的实参是moduleP。exports和module.exports一开始都是指向同一个空对象。

3.最后返回的是module.exports,所以如果在模块文件(foo.js)中把exports或module.exports中的任意一个赋值为一个对象,它俩之间就没任何关系了,只有module.exports对应的对象会被导出。


转载于:https://my.oschina.net/codespring/blog/639430

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值