Node.js模組機制採用了Commonjs規範,彌補了當前JavaScript開發大型沒有標準的缺陷,類似於Java中的類檔案,Python中的import機制,NodeJs中可以通過module.exports、require來匯出和引入一個模組.
在模組載入機制中,NodeJs採用了延遲載入的策略,只有在用到的情況下,系統模組才會被載入,載入完成後會放到binding_cache中。
推薦技術部落格: Node.js技術棧
快速導航
面試指南
require的載入機制?
,參考:模組載入機制module.exports與exports的區別
,參考:module.exports與exports的區別假設有a.js、b.js兩個模組相互引用,會有什麼問題?是否為陷入死迴圈?
,參考:#a模組中的undeclaredVariable變數在b.js中是否會被列印?
,參考:#
模組的分類
系統模組
-
C/C++模組,也叫built-in內建模組,一般用於native模組呼叫,在require出去
-
native模組,在開發中使用的Nodejs的http、buffer、fs等,底層也是呼叫的內建模組(C/C++)。
第三方模組
這裡非Nodejs自帶的模組稱為第三方模組,其實還分為路徑形式的檔案模組(以
.
、..
、/
開頭的)和自定義的模組(比如express、koa框架、moment.js等)
-
javaScript模組:例如
hello.js
-
json模組:例如
hello.json
-
C/C++模組:編譯之後副檔名為.node的模組,例如
hello.node
目錄結構
├── benchmark 一些nodejs效能測試程式碼 ├── deps nodejs依賴 ├── doc 文件 ├── lib nodejs對外暴露的js模組原始碼 ├── src nodejs的c/c++原始碼檔案,內建模組 ├── test 單元測試 ├── tools 編譯時用到的工具 ├── doc api文件 ├── vcbuild.bat win平臺makefile檔案 ├── node.gyp node-gyp構建編譯任務的配置檔案 ...
模組載入機制
面試中可能會問到能說下require的載入機制嗎?
在Nodejs中模組載入一般會經歷3個步驟,路徑分析
、檔案定位
、編譯執行
。
按照模組的分類,按照以下順序進行優先載入:
-
系統快取:模組被執行之後會會進行快取,首先是先進行快取載入,判斷快取中是否有值。
-
系統模組:也就是原生模組,這個優先順序僅次於快取載入,部分核心模組已經被編譯成二進位制,省略了
路徑分析
、檔案定位
,直接載入到了記憶體中,系統模組定義在Node.js原始碼的lib目錄下,可以去檢視。 -
檔案模組:優先載入
.
、..
、/
開頭的,如果檔案沒有加上副檔名,會依次按照.js
、.json
、.node
進行副檔名補足嘗試,那麼在嘗試的過程中也是以同步阻塞模式來判斷檔案是否存在,從效能優化的角度來看待,.json
、.node
最好還是加上檔案的副檔名。 -
目錄做為模組:這種情況發生在檔案模組載入過程中,也沒有找到,但是發現是一個目錄的情況,這個時候會將這個目錄當作一個
包
來處理,Node這塊採用了Commonjs規範,先會在專案根目錄查詢package.json檔案,取出檔案中定義的main屬性("main": "lib/hello.js")
描述的入口檔案進行載入,也沒載入到,則會丟擲預設錯誤:Error: Cannot find module 'lib/hello.js'
-
node_modules目錄載入:對於系統模組、路徑檔案模組都找不到,Node.js會從當前模組的父目錄進行查詢,直到系統的根目錄
模組迴圈引用
問題1
假設有a.js、b.js兩個模組相互引用,會有什麼問題?是否為陷入死迴圈?看以下例子:
a.js
console.log('a模組start');
exports.test = 1;
undeclaredVariable = 'a模組未宣告變數'
const b = require('./b');
console.log('a模組載入完畢: b.test值:',b.test);
複製程式碼
b.js
console.log('b模組start');
exports.test = 2;
const a = require('./a');
console.log('undeclaredVariable: ', undeclaredVariable);
console.log('b模組載入完畢: a.test值:', a.test);
複製程式碼
問題2
問題2: a模組中的undeclaredVariable變數在b.js中是否會被列印?
控制檯執行node a.js
,檢視輸出結果:
a模組start
b模組start
undeclaredVariable: a模組未宣告變數
b模組載入完畢: a.test值: 1
a模組載入完畢: b.test值: 2
複製程式碼
問題1,啟動a.js
的時候,會載入b.js
,那麼在b.js
中又載入了a.js
,但是此時a.js
模組還沒有執行完,返回的是一個a.js
模組的exports
物件未完成的副本
給到b.js
模組。然後b.js
完成載入之後將exports
物件提供給了a.js
模組
問題2,因為undeclaredVariable
是一個未宣告的變數,也就是一個掛在全域性的變數,那麼在其他地方當然是可以拿到的。
在執行程式碼之前,Node.js會使用一個程式碼封裝器進行封裝,例如下面所示:
(function(exports, require, module, __filename, __dirname) {
// 模組的程式碼
});
複製程式碼
exports與moduleexports的區別
exports與module.exports的區別
exports相當於module.exports 的快捷方式如下所示:
const exports = modules.exports;
複製程式碼
但是要注意不能改變exports的指向,我們可以通過 exports.test = 'a'
這樣來匯出一個物件, 但是不能向下面示例直接賦值,這樣會改變exports的指向
//錯誤的寫法 將會得到undefined
exports = {
'a': 1,
'b': 2
}
//正確的寫法
modules.exports = {
'a': 1,
'b': 2
}
複製程式碼
更好的理解之間的關係,可以參考JavaScript中的物件引用
作者:五月君
連結:www.imooc.com/article/284…
來源:慕課網
Github: Node.js技術棧