Node的初学者(包括我自己)都很疑惑,为什么有了exports还要module.exports?两者都有什么区别?
弄清楚上面两个问题之前先思考一个问题。来看一个栗子。
let a = 1
let fibonaci = function(n,first=1,sum=1){
if (n==1||n==2)
return sum
else{
let t = sum;
sum = first+sum
first = t
return fibonaci(n-1,first,sum)
}
}
exports.fibonaci = fibonaci
let fn = require('./module_1')
console.log(fn.fibonaci(5))
很简单的一个模块暴露变量,一个引用。node中使用的语言也是js,我们知道只有对象才能设置属性。那么export没有声明,它为什么可以设置属性呢?
《深入浅出node.js》中写到,每一个文件模块都是一个对象。它的定义如下。
function Module(id, parent) {
this.id=id
this.exports = {}
this.parent = parent;
if (parent&&parent.children){
parent.children.push(this)
}
this.filename = null
this.loaded = false
this.children = []
}
不用去看id巴拉巴拉parent什么的。看到this.exports={}。所以说,所有文件模块都是这个Module的实例对象。自然也就会有exports属性。
而书中也写到。node会对获取到的JavaScript内容进行首尾包装。
所以一个上文提到的的模块会被包装成下面这样
(function (exports,require,module,_filename,_dirname) {
let fibonaci = function(n,first=1,sum=1){
if (n==1||n==2)
return sum
else{
let t = sum;
sum = first+sum
first = t
return fibonaci(n-1,first,sum)
}
}
exports.fibonaci = fibonaci
})
这样就可以解释了为什么js中没有定义require和exports却可以使用,也很好的解释了引入的变量名不冲突的问题(因为实际上引入的变量被封装在一个函数中,可以看成是一个局部变量。局部变量自然不会覆盖掉全局变量)
言归正传。exports和module,exports的区别。
在node中,模块可以把想暴露出去的变量放在module.exports中,这样就可以被其他模块所引用到。而exports只是一个指针。指向module.exports这个对象。
所以说,一开始所有模块暴露都是module.exports,它是一个空对象。
let fibonaci = function(n,first=1,sum=1){
if (n==1||n==2)
return sum
else{
let t = sum;
sum = first+sum
first = t
return fibonaci(n-1,first,sum)
}
}
注意此处,我定义了一个函数,但是没有exports,也就是说没有暴露出去,我们来看看其他模块引用这个模块时的输出。
let fn = require('./module_1')
console.log(fn)
不出所料。上面已经说过,exports是指向的module.export的引用,所以说以下这两种写法得到的结果会完全一样。
module.exports.fibonaci = fibonaci
exports.fibonaci = fibonaci
那他们的差别是什么呢?仔细的分析expors和module.exports的定义。exports是一个指针,指向module.exports,而只有在module.exports中的内容才会暴露给其他模块。所以说以下会得到不一样的结果
首先是module.exports
module.exports='123'
此模块暴露了123,所以其他模块引用这个模块只能拿到123
没有问题。再来看看exports
exports=123
原来的exports是指向module.exports,现在exports变成了123,不指向module.exports了。但是重点是只有module.exports中的内容才会被暴露。所以不管exports变成什么。都不会影响到模块的暴露内容。其他模块拿到的还是空对象。
完美!