commonJS概述php
全部代碼都運行在模塊做用域,不會污染全局做用域。
模塊能夠屢次加載,可是隻會在第一次加載時運行一次,而後運行結果就被緩存了,之後再加載,就直接讀取緩存結果。要想讓模塊再次運行,必須清除緩存。
模塊加載的順序,按照其在代碼中出現的順序。html
2.module對象node
每一個模塊內部,都有一個module對象,表明當前模塊。它有如下屬性。編程
1json |
|
2.1 module.exports屬性瀏覽器
module.exports屬性表示當前模塊對外輸出的接口,其餘文件加載該模塊,實際上就是讀取module.exports變量。緩存
2.2 exports變量bash
爲了方便,Node爲每一個模塊提供一個exports變量,指向module.exports。這等同在每一個模塊頭部,有一行這樣的命令:服務器
1 |
|
這樣作的好處是,在對外輸出模塊接口時,能夠向exports對象添加方法暴露出去。
所以若是改變了module.exports,但還想使用export.xxx的方式暴露一些東西,那就只好咱們本身來寫exports = module.exports;
慄:
1 |
|
3.AMD規範與CommonJS規範的兼容性
CommonJS規範加載模塊是同步的,只有加載完成,才能執行後面的操做。AMD規範則是非同步加載模塊,容許指定回調函數。因爲Node.js主要用於服務器編程,模塊文件通常都已經存在於本地硬盤,因此加載起來比較快,不用考慮非同步加載的方式,因此CommonJS規範比較適用。可是,若是是瀏覽器環境,要從服務器端加載模塊,這時就必須採用非同步模式,所以瀏覽器端通常採用AMD規範。
1 2 3 4 5 |
|
AMD規範容許輸出的模塊兼容CommonJS規範,這時define方法須要寫成下面這樣:
1 2 3 4 5 6 7 8 |
|
4.require命令
4.1基本用法
Node使用CommonJS模塊規範,內置的require命令用於加載模塊文件。
require命令的基本功能是,讀入並執行一個JavaScript文件,而後返回該模塊的exports對象。若是沒有發現指定模塊,會報錯。(說白了就是將另外一個文件中暴露的值,引用到本文件中。)
1 2 3 4 5 |
|
運行下面的命令,能夠輸出exports對象。
1 |
|
若是模塊輸出的是一個函數,那就不能定義在exports對象上面,而要定義在module.exports變量上面。
1 2 |
|
上面代碼中,require命令調用自身,等因而執行module.exports,所以會輸出 hello world。
4.2加載規則
require命令用於加載文件,後綴名默認爲.js。
1 |
|
根據參數的不一樣格式,require命令去不一樣路徑尋找模塊文件:
(1)若是參數字符串以「/」開頭,則表示加載的是一個位於絕對路徑的模塊文件。好比,require('/home/marco/foo.js')將加載/home/marco/foo.js。
(2)若是參數字符串以「./」開頭,則表示加載的是一個位於相對路徑(跟當前執行腳本的位置相比)的模塊文件。好比,require('./circle')將加載當前腳本同一目錄的circle.js。
(3)若是參數字符串不以「./「或」/「開頭,則表示加載的是一個默認提供的核心模塊(位於Node的系統安裝目錄中),或者一個位於各級node_modules目錄的已安裝模塊(全局安裝或局部安裝)。
舉例來講,腳本/home/user/projects/foo.js執行了require('bar.js')命令,Node會依次搜索如下文件。
1 2 3 4 5 |
|
這樣設計的目的是,使得不一樣的模塊能夠將所依賴的模塊本地化。
(4)若是參數字符串不以「./「或」/「開頭,並且是一個路徑,好比require('example-module/path/to/file'),則將先找到example-module的位置,而後再以它爲參數,找到後續路徑。
(5)若是指定的模塊文件沒有發現,Node會嘗試爲文件名添加.js、.json、.node後,再去搜索。.js件會以文本格式的JavaScript腳本文件解析,.json文件會以JSON格式的文本文件解析,.node文件會以編譯後的二進制文件解析。
(6)若是想獲得require命令加載的確切文件名,使用require.resolve()方法。
4.3目錄的加載規則
一般,咱們會把相關的文件會放在一個目錄裏面,便於組織。這時,最好爲該目錄設置一個入口文件,讓require方法能夠經過這個入口文件,加載整個目錄。
在目錄中放置一個package.json文件,而且將入口文件寫入main字段。下面是一個例子。
1 2 |
|
4.4模塊的緩存
第一次加載某個模塊時,Node會緩存該模塊。之後再加載該模塊,就直接從緩存取出該模塊的module.exports屬性。
1 |
|
上面代碼中,連續三次使用require命令,加載同一個模塊。第二次加載的時候,爲輸出的對象添加了一個message屬性。可是第三次加載的時候,這個message屬性依然存在,這就證實require命令並無從新加載模塊文件,而是輸出了緩存。
若是想要屢次執行某個模塊,可讓該模塊輸出一個函數,而後每次require這個模塊的時候,從新執行一下輸出的函數。
全部緩存的模塊保存在require.cache之中,若是想刪除模塊的緩存,能夠像下面這樣寫。
1 2 |
|
注意,緩存是根據絕對路徑識別模塊的,若是一樣的模塊名,可是保存在不一樣的路徑,require命令仍是會從新加載該模塊。
4.5環境變量NODE_PATH
Node執行一個腳本時,會先查看環境變量NODE_PATH。它是一組以冒號分隔的絕對路徑。在其餘位置找不到指定模塊時,Node會去這些路徑查找。
1 2 |
|
因此,若是遇到複雜的相對路徑,好比下面這樣:
1 |
|
有兩種解決方法,一是將該文件加入node_modules目錄,二是修改NODE_PATH環境變量,package.json文件能夠採用下面的寫法。
1 2 |
|
NODE_PATH是歷史遺留下來的一個路徑解決方案,一般不該該使用,而應該使用node_modules目錄機制。
4.6模塊的循環加載
若是發生模塊的循環加載,即A加載B,B又加載A,則B將加載A的不完整版本。
1 2 3 |
|
上面代碼是三個JavaScript文件。其中,a.js加載了b.js,而b.js又加載a.js。這時,Node返回a.js的不完整版本,因此執行結果以下。(也就是說,雖然這樣去require看似會形成a.js和b.js循環引用,但commonJS會在將循環的點剪斷循環,並對剪斷處所在的a.js終止執行,b獲得了第一個x值。)
1 2 3 4 5 |
|
修改main.js,再次加載a.js和b.js。
1 |
|
執行上面代碼,結果以下。
1 2 3 4 5 6 7 |
|
上面代碼中,第二次加載a.js和b.js時,會直接從緩存讀取exports屬性,因此a.js和b.js內部的console.log語句都不會執行了。
4.7 require.main
require方法有一個main屬性,能夠用來判斷模塊是直接執行,仍是被調用執行。
直接執行的時候(node module.js),require.main屬性指向模塊自己。
1 |
|
調用執行的時候(經過require加載該腳本執行),上面的表達式返回false。