require時,exports和module.exports的區別你真的懂嗎?

面試會問

require 的運行機制和緩存策略你瞭解嗎?javascript

require 加載模塊的是同步仍是異步?談談你的理解java

exports 和 module.exports 的區別是什麼?node

require 加載模塊的時候加載的到底是什麼?git

做者簡介:koala,專一完整的 Node.js 技術棧分享,從 JavaScript 到 Node.js,再到後端數據庫,祝您成爲優秀的高級 Node.js 工程師。【程序員成長指北】做者,Github 博客開源項目 github.com/koala-codin…程序員

require

提到 exports 和 module.exports 咱們不得不提到 require 關鍵字。你們都知道 Node.js 遵循 CommonJS 規範,使用 require 關鍵字來加載模塊。github

require 重複引入問題

問題:不知道小夥伴們在使用 require 引入模塊的時候有沒有相關,多個代碼文件中屢次引入相同的模塊會不會形成重複呢?面試

由於在 C++ 中一般使用#IFNDEF等關鍵字來避免文件的重複引入,可是在 Node.js 中無需關心這一點,由於 Node.js 默認先從緩存中加載模塊,一個模塊被加載一次以後,就會在緩存中維持一個副本,若是遇到重複加載的模塊會直接提取緩存中的副本,也就是說在任什麼時候候每一個模塊都只在緩存中有一個實例。數據庫

require 加載模塊的時候是同步仍是異步?

先回答問題,同步的! 可是面試官要是問你爲何是同步仍是異步的呢? 其實這個答案並非固定的,可是小夥伴們能夠經過這幾方面給面試官解釋。後端

  1. 一個做爲公共依賴的模塊,固然想一次加載出來,同步更好
  2. 模塊的個數每每是有限的,並且 Node.js 在 require 的時候會自動緩存已經加載的模塊,再加上訪問的都是本地文件,產生的IO開銷幾乎能夠忽略。

require() 的緩存策略

Node.js 會自動緩存通過 require 引入的文件,使得下次再引入不須要通過文件系統而是直接從緩存中讀取。不過這種緩存方式是通過文件路徑定位的,即便兩個徹底相同的文件,可是位於不一樣的路徑下,會在緩存中維持兩份。 能夠經過緩存

console.log(require.cache)
複製代碼

獲取目前在緩存中的全部文件。

exports 與 module.exports 區別

js文件啓動時

在一個 node 執行一個文件時,會給這個文件內生成一個 exports 和 module 對象, 而module又有一個 exports 屬性。他們之間的關係以下圖,都指向一塊{}內存區域。

exports = module.exports = {};
複製代碼

看一張圖理解這裏更清楚:

require()加載模塊

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,自動拉你入羣。

相關文章
相關標籤/搜索