require 的運行機制和緩存策略你瞭解嗎?javascript
require 加載模塊的是同步仍是異步?談談你的理解java
exports 和 module.exports 的區別是什麼?node
require 加載模塊的時候加載的到底是什麼?git
做者簡介:koala,專一完整的 Node.js 技術棧分享,從 JavaScript 到 Node.js,再到後端數據庫,祝您成爲優秀的高級 Node.js 工程師。【程序員成長指北】做者,Github 博客開源項目 github.com/koala-codin…程序員
提到 exports 和 module.exports 咱們不得不提到 require 關鍵字。你們都知道 Node.js 遵循 CommonJS 規範,使用 require 關鍵字來加載模塊。github
問題:不知道小夥伴們在使用 require 引入模塊的時候有沒有相關,多個代碼文件中屢次引入相同的模塊會不會形成重複呢?面試
由於在 C++ 中一般使用#IFNDEF等關鍵字來避免文件的重複引入,可是在 Node.js 中無需關心這一點,由於 Node.js 默認先從緩存中加載模塊,一個模塊被加載一次以後,就會在緩存中維持一個副本,若是遇到重複加載的模塊會直接提取緩存中的副本,也就是說在任什麼時候候每一個模塊都只在緩存中有一個實例。數據庫
先回答問題,同步的! 可是面試官要是問你爲何是同步仍是異步的呢? 其實這個答案並非固定的,可是小夥伴們能夠經過這幾方面給面試官解釋。後端
Node.js 會自動緩存通過 require 引入的文件,使得下次再引入不須要通過文件系統而是直接從緩存中讀取。不過這種緩存方式是通過文件路徑定位的,即便兩個徹底相同的文件,可是位於不一樣的路徑下,會在緩存中維持兩份。 能夠經過緩存
console.log(require.cache)
複製代碼
獲取目前在緩存中的全部文件。
在一個 node 執行一個文件時,會給這個文件內生成一個 exports 和 module 對象, 而module又有一個 exports 屬性。他們之間的關係以下圖,都指向一塊{}內存區域。
exports = module.exports = {};
複製代碼
看一張圖理解這裏更清楚:
require()加載模塊的時候咱們來看一段實例代碼
//koala.js
let a = '程序員成長指北';
console.log(module.exports); //能打印出結果爲:{}
console.log(exports); //能打印出結果爲:{}
exports.a = '程序員成長指北哦哦'; //這裏辛苦勞做幫 module.exports 的內容給改爲 {a : '程序員成長指北哦哦'}
exports = '指向其餘內存區'; //這裏把exports的指向指走
//test.js
const a = require('/koala');
console.log(a) // 打印爲 {a : '程序員成長指北哦哦'}
複製代碼
看上面代碼的打印結果,應該能獲得這樣的結論:
require導出的內容是module.exports的指向的內存塊內容,並非exports的。 簡而言之,區分他們之間的區別就是 exports 只是 module.exports的引用,輔助後者添加內容用的。用內存指向的方式更好理解。
看一下官方文檔中exports的應用
咱們常常看到這樣的寫法:exports = module.exports = somethings
複製代碼
上面的代碼等價於:
module.exports = somethings
exports = module.exports
複製代碼
原理很簡單,即 module.exports 指向新的對象時,exports 斷開了與 module.exports 的引用,那麼經過 exports = module.exports 讓 exports 從新指向 module.exports 便可。
建議:在使用的時候更建議你們使用module.exports(根據下面的例子也能得出)
Node.js 認爲每一個文件都是一個獨立的模塊。若是你的包有兩個文件,假設是「a.js」 和「b.js」,而後「b.js」 要使用「a.js」 的功能,「a.js」 必需要經過給 exports 對象增長屬性來暴露這些功能:
// a.js
exports.verifyPassword = function(user, password, done) { ... }
複製代碼
完成這步後,全部須要「a.js」 的都會得到一個帶有「verifyPassword」 函數屬性的對象:
// b.js
require(‘a.js’) // { verifyPassword: function(user, password, done) { ... } }
複製代碼
然而,若是咱們想直接暴露這個函數,而不是讓它做爲某些對象的屬性呢?咱們能夠覆寫 exports 來達到目的,可是咱們絕對不能把它當作一個全局變量:
// a.js
module.exports = function(user, password, done) { ... }
複製代碼
注意到咱們是把「exports」 當作 module 對象的一個屬性。「module.exports」 和「exports」 這之間區別是很重要的,並且常常會使 Node.js 新手踩坑。
加入咱們一塊兒學習吧!
node學習交流羣交流羣滿100人不能自動進羣, 請添加羣助手微信號:【coder_qi】備註node,自動拉你入羣。