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變成什麼。都不會影響到模塊的暴露內容。其他模塊拿到的還是空對象。
完美!