原文鏈接:無出處
Node.js模塊 require和 exports
什麼是模塊?
node.js通過實現CommonJS的Modules/1.0標準引入了模塊(module)概念,模塊是Node.js的基本組成部分.一個node.js文件就是一個模塊,也就是說文件和模塊是一一對應的關係.這個文件可以是JavaScript代碼,JSON或者編譯過的C/C++擴展.
Node.js的模塊分爲兩類,一類爲原生(核心)模塊,一類爲文件模塊。
在文件模塊中,又分爲3類模塊。這三類文件模塊以後綴來區分,Node.js會根據後綴名來決定加載方法。
Node.提供了exports和require兩個對象,其中exports是模塊公開的接口,require用於從外部獲取一個模塊接口,即所獲取模塊的exports對象.
require查找策略
原生模塊在Node.js源代碼編譯的時候編譯進了二進制執行文件,加載的速度最快。另一類文件模塊是動態加載的,加載速度比原生模塊慢。但是Node.js對原生模塊和文件模塊都進行了緩存,於是在第二次require時,是不會有重複開銷的。儘管require方法極其簡單,但是內部的加載卻是十分複雜的,其加載優先級也各自不同。
require方法接受以下幾種參數的傳遞:
- http、fs、path等,原生模塊。
- ./mod或../mod,相對路徑的文件模塊。
- /pathtomodule/mod,絕對路徑的文件模塊。
- mod,非原生模塊的文件模塊。
當require一個文件模塊時,從當前文件目錄開始查找node_modules目錄;然後依次進入父目錄,查找父目錄下的node_modules目錄;依次迭代,直到根目錄下的node_modules目錄。
簡而言之,如果require絕對路徑的文件,查找時不會去遍歷每一個node_modules目錄,其速度最快。其餘流程如下:
- 從module path數組中取出第一個目錄作爲查找基準。
- 直接從目錄中查找該文件,如果存在,則結束查找。如果不存在,則進行下一條查找。
- 嘗試添加.js、.json、.node後綴後查找,如果存在文件,則結束查找。如果不存在,則進行下一條。
- 嘗試將require的參數作爲一個包來進行查找,讀取目錄下的package.json文件,取得main參數指定的文件。
- 嘗試查找該文件,如果存在,則結束查找。如果不存在,則進行第3條查找。
- 如果繼續失敗,則取出module path數組中的下一個目錄作爲基準查找,循環第1至5個步驟。
- 如果繼續失敗,循環第1至6個步驟,直到module path中的最後一個值。
- 如果仍然失敗,則拋出異常。
module.exports還是exports
我們首先通過一個例子來介紹exports的作用.首先新建一個模塊calc.js,代碼如下:
1 |
var add = function(a,b){ return a + b; }; var minus = function(a,b){ return a - b; }; |
再新建一個test.js文件,代碼如下 ,
1 |
var calc = require("./calc"); console.log(calc.add(1,2)); |
然後我們在Terminal中執行,發出現如下出錯.
/Users/Liuzc/Desktop/node/text.js:3
console.log(calc.add(1,2));
^
TypeError: Object # has no method ‘add’
at Object. (/Users/Liuzc/Desktop/node/text.js:3:18)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:901:3
這時我們修改一下calc.js的代碼:
1 |
var add = function(a,b){ return a + b; }; var minus = function(a,b){ return a - b; }; exports.add = add; exports.minus = minus; |
再次在Terminal中執行node test.js時.正確的顯示了結果.
Liuzcs-MacBook-Pro:node Liuzc$ node text.js
3
一個模塊可以通過module.exports或exports將函數、變量等導出,以使其它JavaScript腳本通過require()函數引入並使用。
那麼,到底應該用module.exports還是用exports呢?我們先看下面的一個例子:
1 2 3 4 5 6 7 |
console.log(this); console.log(exports); console.log(module.exports); console.log(this === exports); console.log(this === module.exports); console.log(exports === module.exports); |
執行結果是:
{}
{}
{}
true
true
也就是說,exports默認和module.exports指向同一個空對象。
再看一個例子.calc.js中有代碼如下 :
1 2 3 4 5 6 7 |
exports.multiply = function(a,b){ return a * b; } module.exports.multiply = function(a,b){ return a - b; } |
這時猜猜執行test.js中的如下代碼的結果將會是什麼?
1 2 |
var calc = require("./calc"); console.log(calc.multiply(4,2)); |
對, 結果是2, 而不是8.也就是說如果運行時讓exports、this和module.exports指向不同的對象,只有module.exports指向的對象纔回被導出。module.exports纔是真正的接口,exports只不過是它的一個輔助工具。 最終返回給調用的是module.exports而不是exports。 所有的exports收集到的屬性和方法,都賦值給了module.exports。當然,這有個前提,就是module.exports本身不具備任何屬性和方法。如果,module.exports已經具備一些屬性和方法,那麼exports收集來的信息將被忽略。
如果你想你的模塊是一個特定的類型就用module.exports。如果你想的模塊是一個典型的”實例化對象”就用exports。