爲了讓node.js的文件能夠相互調用,node.js提供了一個簡單的模塊系統。模塊是node.js應用程序基本的組成部分,文件和模塊是一一對應的。換言之,一個node.js文件就是一個模塊,這個文件多是javascript代碼、json或者編譯過的c/c++擴展。javascript
其中http、fs、net等都是node.js提供的核心模塊,使用c/c++實現,外部用javascript封裝。html
建立模塊有兩種方式,java
經過exports建立node
經過module.exports建立c++
node.js中,建立一個模塊很是簡單,咱們建立一個main.js文件,它引用了hello模塊,代碼以下,npm
var hello = require('./hello') hello.world()
在上面的代碼中,require('./hello')引入了當前目錄下的hello.js文件。編程
./表明當前目錄,node.js默認後綴爲js。json
node.js提供了exports和require兩個對象,其中exports是模塊公開的接口,require用於從外部獲取一個模塊的接口,即所獲取模塊的exports對象。函數
接下來咱們建立hello.js文件,以下代碼所示,工具
exports.world = function() { console.log('hello world') }
以上示例中,hello.js經過exports對象把world做爲模塊的訪問接口,在main.js中經過require('./hello')加載這個模塊,而後就能夠直接訪問hello.js中exports對象的成員函數了。
有時候咱們只是想把一個對象封裝到模塊中,以下格式,
module.exports = function() { }
以上面的格式,來寫一個模塊,以下hello.js代碼,
function Hello() { var name; this.setName = function(thyName) { name = thyName } this.sayHello = function() { console.log('hello ' + name) } } module.exports = Hello
這樣就能夠直接獲取這個對象了,以下main.js代碼,
// main.js var Hello = require('./hello') hello = new Hello() hello.setName('BYVoid') hello.sayHello()
模塊接口的惟一變化是使用module.exports = Hello代替了exports.world = function() {}。在外部引用該模塊時,其接口對象就是要輸出的Hello對象自己,而不是原先的exports。
爲了更好地解釋exports和module.exports之間的關係,先經過一個簡單的js示例來作一個說明,以下代碼,
var a = {name: 1} var b = a console.log(a) console.log(b) b.name = 2 console.log(a) console.log(b) b = {name: 3} console.log(a) console.log(b)
運行test.js結果爲,
{ name: 1 } { name: 1 } { name: 2 } { name: 2 } { name: 2 } { name: 3 }
簡單解釋一下上面的代碼:a是一個對象,b是對a對象的引用,此時a和b只想同一塊內存,因此前兩個輸出同樣;當對b作修改時,則a和b只想同一塊內存地址的內容發生了改變,因此a的值改變也體現了出來;當b被覆蓋時,b只想了一塊新的內存,而a仍是隻想原來的內存,因此最後兩個輸出不同。
明白了上面的例子以後,只須要指點3點就能瞭解exports和module.exports的區別了,
module.exports初始值爲一個空對象{}
exports是隻想module.exports的引用
require()返回的是module.exports而不是exports
也就是說,module.exports纔是真正的接口,exports只不過是它的一個輔助工具。最攻返回給調用者的是module.exports而不是exports。
再強調一點,在node.js中,一個文件對應一個模塊。爲了方便,模塊中會有一個exports對象,它和module.exports指向同一個變量,因此咱們修改exports對象的時候也會修改module.exports對象;當咱們經過賦值方式爲module.exports賦值時候,此時module.exports與exports對象指向的變量就不一樣了,因此不管exports對象怎麼改,都和module.exports對象沒有任何關係了。
加粗!加粗!加粗!通常來講,推薦使用module.exports,儘可能少使用exports。
在node.js中模塊有兩種類型,即,
核心模塊
文件模塊
核心模塊直接使用名稱獲取,例如常常使用的http模塊,使用以下代碼獲取,
var http = require('http') ... http.createServer()
簡要描述一下上面的代碼,node.js中自帶了一個叫作http的模塊,在上述代碼中咱們請求它並把返回的值賦值給一個本地變量,這樣本地變量就編程了一個擁有全部http模塊所提供的公共方法的對象。
在前面建立模塊的demo中,經過require('./hello')語法,以下代碼,
var Hello = require('./hello') hello = new Hello() hello.setName('BYVoid') ... ...
這裏,咱們使用./test來獲取自定義文件模塊,這種經過相對路徑或絕對路徑是文件模塊的搜索方式。
node.js加載模塊時,遵循了以下的加載規則,
核心模塊優先級最高,直接使用名字加載,再有命名衝突的時候首先加載核心模塊
文件模塊只能按照路徑加載 -- 相對路徑或絕對路徑,而且能夠省略默認的.js後綴名
查找node_modules目錄,當咱們在調用npm install <name>命令的時候,會在當前目錄下建立node_module目錄來安裝模塊,當require遇到一個既不是核心模塊,又不是以路徑形式表示的模塊名稱時,會試圖在當前目錄下的node_modules目錄中查找是否是有這樣一個模塊。若是沒有找到,則會在當前目錄的上一層的node_modules目錄中繼續查找,反覆執行這一過程,知道遇到根目錄位置。
相對路徑 - 例如:
./hello
表示同級目錄,../hello
表示上層目錄絕對路徑 - 例如:
/Users/user/Desktop/js/hello