nodejs所谓的模块就是一个文件,或者是匿名函数。(CommonJs)
require
exports
module.exports。
为什么直接console.log(typeof require('*****')) # 是一个string类型?
但是使用的话,又是一个对象函数?
https://github.com/nodejs/node/blob/v5.x/lib/internal/bootstrap_node.js
https://github.com/nodejs/node/blob/v5.x/lib/module.js
复制代码
https://github.com/nodejs/node/blob/v5.x/lib/module.js
通过对源码的查看:
364行列:
Module.prototype.require = function(path) {
assert(path, 'missing path');
assert(typeof path === 'string', 'path must be a string');
return Module._load(path, this, /* isMain */ false);
};
复制代码
Module._load = function(request, parent, isMain) {
if (parent) {
debug('Module._load REQUEST %s parent: %s', request, parent.id);
}
var filename = Module._resolveFilename(request, parent);
复制代码
# module._compile:
Module.prototype._compile = function(content, filename) {
var self = this;
var args = [self.exports, require, self, filename, dirname];
return compiledWrapper.apply(self.exports, args);
};
复制代码
得出以下结论
require 一个文件的时候,会对其进行进行一个匿名函数的包装。
(function (exports, require, module, __filename, __dirname) {
// 模块源码
});
复制代码
requier顺序:
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
复制代码
exports和module.exports的区别
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
function someFunc() {}
exports = someFunc;
module.exports = someFunc;
})(module, module.exports);
return module.exports;
}
复制代码
exports = module.exports,所以说,一开始这两个东西指向同一个对象实例
exports.x = function() {
//在exports上添加了一个方法,module.exports也添加了同样的方法,因为它们指向了同样的对象实例
console.log("Hi!");
}
module.exports.x();
复制代码
总结
# 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就走到一起了。
复制代码