我们在使用node.js中经常会使用到exports和module.exports对模块中的方法进行导出,在其他模块进行使用。但是他们会有什么区别呢?或者说我们有了解模块吗?本文主要介绍一下Module模块
一 、模块是什么?
在js的源码load.js中,我们可以看到模块其实是一个Module对象。
function Module(id = '', parent) {
this.id = id;
this.path = path.dirname(id);
this.exports = {};
this.parent = parent;
updateChildren(parent, this, false);
this.filename = null;
this.loaded = false;
this.children = [];
}
解析:
module.id 模块的识别符,通常是带有绝对路径的模块文件名。
module.filename 模块的文件名,带有绝对路径。
module.loaded 返回一个布尔值,表示模块是否已经完成加载。
module.parent 返回一个对象,表示调用该模块的模块。
module.children 返回一个数组,表示该模块要用到的其他模块。
module.exports 表示模块对外输出的值。
二、 require的模块加载机制
我们经常会使用require在某个文件中引入其他模块中exports和module.exports的方法,下面我们通过源码来了解一下require。首先require在源码中调用的 Module._load 方法。
// require 其实内部调用 Module._load 方法
Module._load = function(request, parent, isMain) {
// 计算绝对路径
var filename = Module._resolveFilename(request, parent);
// 第一步:如果有缓存,取出缓存
var cachedModule = Module._cache[filename];
if (cachedModule) {
return cachedModule.exports;
// 第二步:是否为内置模块
if (NativeModule.exists(filename)) {
return NativeModule.require(filename);
}
/********************************这里注意了**************************/
// 第三步:生成模块实例,存入缓存
// 这里的Module就是我们上面的1.1定义的Module
var module = new Module(filename, parent);
Module._cache[filename] = module;
/********************************这里注意了**************************/
// 第四步:加载模块
// 下面的module.load实际上是Module原型上有一个方法叫Module.prototype.load
try {
module.load(filename);
hadException = false;
} finally {
if (hadException) {
delete Module._cache[filename];
}
}
// 第五步:输出模块的exports属性
return module.exports;
};
总结:requre主要是通过文件的路径 - 找到对应的Module对象(没有则创建)- 返回对应module对象的exports对象。
三、exports和module.exports
结论就是
1.exports其实是指向module.exports的。
2.exports和module.exports都是往对应的Module对象的exports对象中添加数据。
3.module.exports可以直接导出一个对象或者一个常量等
module.exports = {
test:"test"
};
module.exports = "123"
module.exports = new hello()
在开发Node.js应用的时候,很多模块都是需要引入才能使用,但是为什么exports和module.exports我们没有引用却可以直接使用呢?
事实上,Node.js应用在编译的过程中会对JavaScript文件的内容进行头尾的封装。例如:
// hello.js
const hello = function () {
console.log('Hello world');
}
module.exports = {
hello
}
// 头尾封装后的js代码
(function (exports, require, module, __filename, __dirname) {
const hello = function () {
console.log('Hello world');
}
module.exports = {
hello
}
})
在进行了头尾封装之后,各个模块之间进行了作用域隔离,避免了污染全局变量,同时可以使每个模块在不引入这些变量的情况下可以使用它们。这些变量依次为当前模块的exports属性、require()方法、当前模块自身(module)、在文件系统中的完整路径、文件目录。
//hello.js
const hello = function () {
console.log('Hello world');
}
module.exports = {
hello
}
console.error(module);
Module {
id: '.',
exports: { hello: [Function: hello] },
parent: null,
filename: 'C:\\Users\\WebstormProjects\\study\\bin\\hello.js',
loaded: false,
children: [],
paths:
[ 'C:\\Users\\WebstormProjects\\study\\bin\\node_modules',
'C:\\Users\\WebstormProjects\\study\\node_modules',
'C:\\Users\\WebstormProjects\\node_modules',
'C:\\Users\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules' ] }
调用文件:
//1212.js
var test = require("./hello").hello;
test();
console.error(module);
Hello world
Module {
id: '.',
exports: {},
parent: null,
filename: 'C:\\Users\\WebstormProjects\\study\\bin\\1212.js',
loaded: false,
children:
[ Module {
id: 'C:\\Users\\WebstormProjects\\study\\bin\\hello.js',
exports: [Object],
parent: [Circular],
filename: 'C:\\Users\\WebstormProjects\\study\\bin\\hello.js',
loaded: true,
children: [],
paths: [Array] } ],
paths:
[ 'C:\\Users\\WebstormProjects\\study\\bin\\node_modules',
'C:\\Users\\WebstormProjects\\study\\node_modules',
'C:\\Users\\WebstormProjects\\node_modules',
'C:\\Users\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules' ] }