Node.js 模塊機制

Node.js 模塊機制

Node.js 模塊機制采用了 Commonjs 規(guī)范测暗,彌補了當前 JavaScript 開發(fā)大型應用沒有標準的缺陷诗芜,類似于 Java 中的類文件倦逐,Python 中的 import 機制寝姿,Node.js 中可以通過 module.exports、require 來導出和引入一個模塊.

在模塊加載機制中乙帮,Node.js 采用了延遲加載的策略,只有在用到的情況下极景,系統(tǒng)模塊才會被加載察净,加載完成后會放到 binding_cache 中。

面試指南

  • require的加載機制盼樟?氢卡,參考:模塊加載機制

  • module.exports與exports的區(qū)別,參考:對象引用關系考察

  • 假設有a.js晨缴、b.js兩個模塊相互引用译秦,會有什么問題?是否為陷入死循環(huán)击碗?筑悴,參考正文“模塊循環(huán)引用問題1”

  • a模塊中的undeclaredVariable變量在b.js中是否會被打印延都?雷猪,參考正文“模塊循環(huán)引用問題2”

  • 模塊在require的過程中是同步還是異步?晰房,參考正文模塊加載機制 “文件模塊“

模塊的分類

系統(tǒng)模塊

  • C/C++ 模塊求摇,也叫 built-in 內建模塊,一般用于 native 模塊調用殊者,在 require 出去

  • native 模塊与境,在開發(fā)中使用的 Node.js 的 http、buffer猖吴、fs 等摔刁,底層也是調用的內建模塊 (C/C++)。

第三方模塊

非 Node.js 自帶的模塊稱為第三方模塊海蔽,其實還分為路徑形式的文件模塊(以 .共屈、 ..绑谣、 /開頭的)和自定義的模塊(比如 express、koa 框架拗引、moment.js 等)

  • javaScript 模塊:例如 hello.js

  • json 模塊:例如 hello.json

  • C/C++ 模塊:編譯之后擴展名為 .node 的模塊借宵,例如 hello.node

目錄結構

<pre>├── benchmark 一些 Node.js 性能測試代碼
├── deps Node.js 依賴
├── doc 文檔
├── lib Node.js 對外暴露的 js 模塊源碼
├── src Node.js 的 c/c++ 源碼文件,內建模塊
├── test 單元測試
├── tools 編譯時用到的工具
├── doc api 文檔
├── vcbuild.bat win 平臺 makefile 文件
├── node.gyp node-gyp 構建編譯任務的配置文件
...
</pre>

模塊加載機制

面試中可能會問到能說下require的加載機制嗎?

在 Node.js 中模塊加載一般會經歷 3 個步驟矾削, 路徑分析壤玫、 文件定位編譯執(zhí)行哼凯。

按照模塊的分類欲间,按照以下順序進行優(yōu)先加載:

  • 系統(tǒng)緩存:模塊被執(zhí)行之后會會進行緩存,首先是先進行緩存加載断部,判斷緩存中是否有值猎贴。

  • 系統(tǒng)模塊:也就是原生模塊,這個優(yōu)先級僅次于緩存加載家坎,部分核心模塊已經被編譯成二進制嘱能,省略了 路徑分析文件定位虱疏,直接加載到了內存中惹骂,系統(tǒng)模塊定義在 Node.js 源碼的 lib 目錄下,可以去查看做瞪。

  • 文件模塊:優(yōu)先加載 .对粪、 ../ 開頭的装蓬,如果文件沒有加上擴展名著拭,會依次按照 .js.json牍帚、 .node 進行擴展名補足嘗試儡遮,那么在嘗試的過程中也是以同步阻塞模式來判斷文件是否存在,從性能優(yōu)化的角度來看待暗赶, .json鄙币、 .node最好還是加上文件的擴展名。

  • 目錄做為模塊:這種情況發(fā)生在文件模塊加載過程中蹂随,也沒有找到十嘿,但是發(fā)現(xiàn)是一個目錄的情況,這個時候會將這個目錄當作一個 來處理岳锁,Node 這塊采用了 Commonjs 規(guī)范绩衷,先會在項目根目錄查找 package.json 文件,取出文件中定義的 main 屬性 ("main":"lib/hello.js") 描述的入口文件進行加載,也沒加載到咳燕,則會拋出默認錯誤: Error: Cannot find module 'lib/hello.js'

  • node_modules 目錄加載:對于系統(tǒng)模塊勿决、路徑文件模塊都找不到,Node.js 會從當前模塊的父目錄進行查找招盲,直到系統(tǒng)的根目錄

image

require 模塊加載時序圖

模塊緩存在哪

上面講解了模塊的加載機制剥险,中間有提到模塊初次加載之后會緩存起來,有沒有疑問宪肖,模塊緩存在哪里?

Node.js 提供了 require.cache API 查看已緩存的模塊健爬,返回值為對象控乾,為了驗證,這里做一個簡單的測試娜遵,如下所示:

新建 test-module.js 文件

這里我導出一個變量和一個方法

module.exports={
      a : 1,
      test : () => { }
}

新建 test.js 文件

require('./test-module.js');
console.log(require.cache);

在這個文件里加載 test-module.js 文件蜕衡,在之后打印下 require.cache 看下里面返回的是什么?看到以下結果應該就很清晰了设拟,模塊的文件名慨仿、地址、導出數(shù)據(jù)都很清楚纳胧。

image

模塊循環(huán)引用

問題1

假設有 a.js镰吆、b.js 兩個模塊相互引用,會有什么問題跑慕?是否為陷入死循環(huán)万皿?看以下例子

// 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

a 模塊中的 undeclaredVariable 變量在 b.js 中是否會被打印核行?

控制臺執(zhí)行 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 模塊還沒有執(zhí)行完惩系,返回的是一個 a.js 模塊的 exports 對象 未完成的副本 給到 b.js 模塊(因此是不會陷入死循環(huán)的)位岔。然后 b.js 完成加載之后將 exports 對象提供給了 a.js 模塊

問題2,因為 undeclaredVariable 是一個未聲明的變量蛆挫,也就是一個掛在全局的變量赃承,那么在其他地方當然是可以拿到的。

在執(zhí)行代碼之前悴侵,Node.js 會使用一個代碼封裝器進行封裝瞧剖,例如下面所示:

(function(exports, require, module, __filename, __dirname) {
    // 模塊的代碼
});

對象引用關系考察

也許是面試考察最多的問題:module.exports 與 exports 的區(qū)別?

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 中的對象引用 https://www.nodejs.red/#/javascript/object

Nodejs技術棧


收錄時間: 2019/08/26

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捉撮,隨后出現(xiàn)的幾起案子怕品,更是在濱河造成了極大的恐慌,老刑警劉巖巾遭,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肉康,死亡現(xiàn)場離奇詭異,居然都是意外死亡灼舍,警方通過查閱死者的電腦和手機吼和,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骑素,“玉大人炫乓,你說我怎么就攤上這事∠壮螅” “怎么了末捣?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長创橄。 經常有香客問我箩做,道長,這世上最難降的妖魔是什么妥畏? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任卒茬,我火速辦了婚禮,結果婚禮上咖熟,老公的妹妹穿的比我還像新娘圃酵。我一直安慰自己,他們只是感情好馍管,可當我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布郭赐。 她就那樣靜靜地躺著,像睡著了一般确沸。 火紅的嫁衣襯著肌膚如雪捌锭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天罗捎,我揣著相機與錄音观谦,去河邊找鬼。 笑死桨菜,一個胖子當著我的面吹牛豁状,可吹牛的內容都是我干的捉偏。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼泻红,長吁一口氣:“原來是場噩夢啊……” “哼夭禽!你這毒婦竟也來了?” 一聲冷哼從身側響起谊路,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤讹躯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后缠劝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體潮梯,經...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年惨恭,在試婚紗的時候發(fā)現(xiàn)自己被綠了酷麦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡喉恋,死狀恐怖,靈堂內的尸體忽然破棺而出母廷,到底是詐尸還是另有隱情轻黑,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布琴昆,位于F島的核電站氓鄙,受9級特大地震影響,放射性物質發(fā)生泄漏业舍。R本人自食惡果不足惜抖拦,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望舷暮。 院中可真熱鬧态罪,春花似錦、人聲如沸下面。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沥割。三九已至耗啦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間机杜,已是汗流浹背帜讲。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留椒拗,地道東北人似将。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓获黔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親玩郊。 傳聞我的和親對象是個殘疾皇子肢执,可洞房花燭夜當晚...
    茶點故事閱讀 42,901評論 2 345