前言
之前学习node的时候,没有从官方文档去入手。虽然在用exports和module.exports,网上也了解到一些他们之间的关系,但是当时想象不到内部是怎么实现的。今天看了一下官方的module模块,很多疑问豁然开朗。在此纪念自己走过的那么多弯路。
下面总结的内容,不适合js基础不好的同学。起码得知道js里面的作用域链,js里面面向对象是怎么实现的,还需要了解js语言的内存模型。
Module
官方网址:https://nodejs.org/docs/latest/api/modules.html#
摘录重点:
- 1、在 Node.js 模块系统中,每个文件都被视为独立的模块。
- 2、模块内的本地变量是私有的,因为模块被 Node.js 包装在一个函数中这样也就可以解释,为什么我们可以在每个文件中,调用require,module,__filename等了,因为这些都是包装函数里面的参数呀。
1
2
3(function(exports, require, module, __filename, __dirname) {
// 模块的代码实际上在这里
});
那为什么每个文件的本地变量是私有的呢?因为函数具有独立作用域呀。 - 3、module.exports和exports
我们经常会看到这样的代码:在node的包装器函数中,我们可以看到有exports和module参数。当我们执行1
2
3module.exports = exports = function Constructor() {
// ... code
};console.log(module.exports === exports);//true
结果会返回ture。可以得出其实这俩个变量是指向同一块内存地址。
无论是module.exports,或者是exports,其实都是为了调用require()的时候,处理放出来的变量。官方例子:
1 | function require(/* ... */) { |
可以看到,exports只是module.exports的一个引用。require最后返回的module.exports,如果exports指向了其他对象,那么是无法返回对象的。例如:
1 | module.exports.hello = true; // 从对模块的引用中导出 |
console.log(‘a 开始’);
exports.done = false;
const b = require(‘./b.js’);
console.log(‘在 a 中,b.done = %j’, b.done);
exports.done = true;
console.log(‘a 结束’);
1 | b.js |
console.log(‘b 开始’);
exports.done = false;
const a = require(‘./a.js’);
console.log(‘在 b 中,a.done = %j’, a.done);
exports.done = true;
console.log(‘b 结束’);
1 | main.js |
console.log(‘main 开始’);
const a = require(‘./a.js’);
const b = require(‘./b.js’);
console.log(‘在 main 中,a.done=%j,b.done=%j’, a.done, b.done);
1 | 当 main.js 加载 a.js 时,a.js 又加载 b.js。 此时,b.js 会尝试去加载 a.js。**为了防止无限的循环,会返回一个 a.js 的 exports 对象的 未完成的副本 给 b.js 模块**。 然后 b.js 完成加载,并将 exports 对象提供给 a.js 模块。 |
$ node main.js
main 开始
a 开始
b 开始
在 b 中,a.done = false
b 结束
在 a 中,b.done = true
a 结束
在 main 中,a.done=true,b.done=true
# 模块优先级调用问题
在node中,require其它模块的时候,有好几种方式:
1、从核心模块中引用,比如http,net,fs,dns等模块。
2、文件模块,从文件中引用,比如require('/home/marco/foo.js')
3、目录模块,例如 require('./some-library') 会试图加载:
* ./some-library/index.js
* ./some-library/index.node
* 目录模块还有一种是根据package.json加载。
4、从 node_modules 目录加载
5、从全局目录加载
node中一共有以上5中导入模块的方法。游戏就是从1到5。第2和第3种方式都采用相对路径,可以归为一类。其他几种可以归为一类,都是都通过node本身的机制进行导入。第5种是根据环境变量NODE_PATH进行查找。
# 总结
module其实是每个模块的一个参数,不是全局变量。
# 参考
https://nodejs.org/docs/latest/api/modules.html#