Node.js經常使用模塊Module的加載機制與使用

Node.js模塊機制採用了Commonjs規範,彌補了當前JavaScript開發大型沒有標準的缺陷,相似於Java中的類文件,Python中的import機制,NodeJs中能夠經過module.exports、require來導出和引入一個模塊.javascript

在模塊加載機制中,NodeJs採用了延遲加載的策略,只有在用到的狀況下,系統模塊纔會被加載,加載完成後會放到binding_cache中。java

推薦技術博客: Node.js技術棧node

快速導航

面試指南

  • require的加載機制?,參考:模塊加載機制
  • module.exports與exports的區別,參考:module.exports與exports的區別
  • 假設有a.js、b.js兩個模塊相互引用,會有什麼問題?是否爲陷入死循環?,參考:#
  • a模塊中的undeclaredVariable變量在b.js中是否會被打印?,參考:#

模塊的分類

系統模塊

  • C/C++模塊,也叫built-in內建模塊,通常用於native模塊調用,在require出去c++

  • native模塊,在開發中使用的Nodejs的http、buffer、fs等,底層也是調用的內建模塊(C/C++)。git

第三方模塊

這裏非Nodejs自帶的模塊稱爲第三方模塊,其實還分爲路徑形式的文件模塊(以.../開頭的)和自定義的模塊(好比express、koa框架、moment.js等)github

  • javaScript模塊:例如hello.js面試

  • json模塊:例如hello.jsonexpress

  • C/C++模塊:編譯以後擴展名爲.node的模塊,例如hello.nodejson

目錄結構

├── 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的加載機制嗎?api

在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會從當前模塊的父目錄進行查找,直到系統的根目錄

圖片描述

require模塊加載時序圖

模塊循環引用

問題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技術棧

相關文章
相關標籤/搜索