【轉】Node.js模塊 require和 exports

原文鏈接:無出處

Node.js模塊 require和 exports

 

什麼是模塊?

node.js通過實現CommonJS的Modules/1.0標準引入了模塊(module)概念,模塊是Node.js的基本組成部分.一個node.js文件就是一個模塊,也就是說文件和模塊是一一對應的關係.這個文件可以是JavaScript代碼,JSON或者編譯過的C/C++擴展.

Node.js的模塊分爲兩類,一類爲原生(核心)模塊,一類爲文件模塊。

在文件模塊中,又分爲3類模塊。這三類文件模塊以後綴來區分,Node.js會根據後綴名來決定加載方法。

  • .js。通過fs模塊同步讀取js文件並編譯執行。
  • .node。通過C/C++進行編寫的Addon。通過dlopen方法進行加載。
  • .json。讀取文件,調用JSON.parse解析加載。

Node.提供了exports和require兩個對象,其中exports是模塊公開的接口,require用於從外部獲取一個模塊接口,即所獲取模塊的exports對象.

require查找策略

原生模塊在Node.js源代碼編譯的時候編譯進了二進制執行文件,加載的速度最快。另一類文件模塊是動態加載的,加載速度比原生模塊慢。但是Node.js對原生模塊和文件模塊都進行了緩存,於是在第二次require時,是不會有重複開銷的。儘管require方法極其簡單,但是內部的加載卻是十分複雜的,其加載優先級也各自不同。

nodejs-require

require方法接受以下幾種參數的傳遞:

  • http、fs、path等,原生模塊。
  • ./mod或../mod,相對路徑的文件模塊。
  • /pathtomodule/mod,絕對路徑的文件模塊。
  • mod,非原生模塊的文件模塊。
    當require一個文件模塊時,從當前文件目錄開始查找node_modules目錄;然後依次進入父目錄,查找父目錄下的node_modules目錄;依次迭代,直到根目錄下的node_modules目錄。

簡而言之,如果require絕對路徑的文件,查找時不會去遍歷每一個node_modules目錄,其速度最快。其餘流程如下:

  1. 從module path數組中取出第一個目錄作爲查找基準。
  2. 直接從目錄中查找該文件,如果存在,則結束查找。如果不存在,則進行下一條查找。
  3. 嘗試添加.js、.json、.node後綴後查找,如果存在文件,則結束查找。如果不存在,則進行下一條。
  4. 嘗試將require的參數作爲一個包來進行查找,讀取目錄下的package.json文件,取得main參數指定的文件。
  5. 嘗試查找該文件,如果存在,則結束查找。如果不存在,則進行第3條查找。
  6. 如果繼續失敗,則取出module path數組中的下一個目錄作爲基準查找,循環第1至5個步驟。
  7. 如果繼續失敗,循環第1至6個步驟,直到module path中的最後一個值。
  8. 如果仍然失敗,則拋出異常。

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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章