Node.js 模塊機(jī)制及常見面試問題解答

Node.js 模塊機(jī)制采用了 Commonjs 規(guī)范顽素,彌補(bǔ)了當(dāng)前 JavaScript 開發(fā)大型應(yīng)用沒有標(biāo)準(zhǔn)的缺陷礁竞,類似于 Java 中的類文件,Python 中的 import 機(jī)制厘擂,Node.js 中可以通過 module.exports程腹、require 來導(dǎo)出和引入一個(gè)模塊.

在模塊加載機(jī)制中,Node.js 采用了延遲加載的策略舅逸,只有在用到的情況下桌肴,系統(tǒng)模塊才會(huì)被加載,加載完成后會(huì)放到 binding_cache 中琉历。

面試指南

  • require的加載機(jī)制坠七?,參考:模塊加載機(jī)制

  • module.exports與exports的區(qū)別旗笔,參考:對(duì)象引用關(guān)系考察

  • 假設(shè)有a.js彪置、b.js兩個(gè)模塊相互引用,會(huì)有什么問題蝇恶?是否為陷入死循環(huán)拳魁?,參考正文“模塊循環(huán)引用問題1”

  • a模塊中的undeclaredVariable變量在b.js中是否會(huì)被打哟榛 潘懊?,參考正文“模塊循環(huán)引用問題2”

  • 模塊在require的過程中是同步還是異步贿衍?授舟,參考正文模塊加載機(jī)制 “文件模塊“

模塊的分類

系統(tǒng)模塊

  • C/C++ 模塊,也叫 built-in 內(nèi)建模塊舌厨,一般用于 native 模塊調(diào)用岂却,在 require 出去

  • native 模塊,在開發(fā)中使用的 Node.js 的 http、buffer躏哩、fs 等署浩,底層也是調(diào)用的內(nèi)建模塊 (C/C++)。

第三方模塊

非 Node.js 自帶的模塊稱為第三方模塊扫尺,其實(shí)還分為路徑形式的文件模塊(以 .筋栋、 ../開頭的)和自定義的模塊(比如 express正驻、koa 框架弊攘、moment.js 等)

  • javaScript 模塊:例如 hello.js

  • json 模塊:例如 hello.json

  • C/C++ 模塊:編譯之后擴(kuò)展名為 .node 的模塊,例如 hello.node

目錄結(jié)構(gòu)

<pre>├── benchmark 一些 Node.js 性能測(cè)試代碼
├── deps Node.js 依賴
├── doc 文檔
├── lib Node.js 對(duì)外暴露的 js 模塊源碼
├── src Node.js 的 c/c++ 源碼文件姑曙,內(nèi)建模塊
├── test 單元測(cè)試
├── tools 編譯時(shí)用到的工具
├── doc api 文檔
├── vcbuild.bat win 平臺(tái) makefile 文件
├── node.gyp node-gyp 構(gòu)建編譯任務(wù)的配置文件
...
</pre>

模塊加載機(jī)制

面試中可能會(huì)問到能說下require的加載機(jī)制嗎?

在 Node.js 中模塊加載一般會(huì)經(jīng)歷 3 個(gè)步驟襟交, 路徑分析文件定位伤靠、 編譯執(zhí)行捣域。

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

  • 系統(tǒng)緩存:模塊被執(zhí)行之后會(huì)會(huì)進(jìn)行緩存宴合,首先是先進(jìn)行緩存加載焕梅,判斷緩存中是否有值。

  • 系統(tǒng)模塊:也就是原生模塊卦洽,這個(gè)優(yōu)先級(jí)僅次于緩存加載贞言,部分核心模塊已經(jīng)被編譯成二進(jìn)制,省略了 路徑分析阀蒂、 文件定位该窗,直接加載到了內(nèi)存中,系統(tǒng)模塊定義在 Node.js 源碼的 lib 目錄下蚤霞,可以去查看挪捕。

  • 文件模塊:優(yōu)先加載 ...争便、 / 開頭的级零,如果文件沒有加上擴(kuò)展名,會(huì)依次按照 .js滞乙、 .json奏纪、 .node 進(jìn)行擴(kuò)展名補(bǔ)足嘗試,那么在嘗試的過程中也是以同步阻塞模式來判斷文件是否存在斩启,從性能優(yōu)化的角度來看待序调, .json.node最好還是加上文件的擴(kuò)展名兔簇。

  • 目錄做為模塊:這種情況發(fā)生在文件模塊加載過程中发绢,也沒有找到硬耍,但是發(fā)現(xiàn)是一個(gè)目錄的情況,這個(gè)時(shí)候會(huì)將這個(gè)目錄當(dāng)作一個(gè) 來處理边酒,Node 這塊采用了 Commonjs 規(guī)范经柴,先會(huì)在項(xiàng)目根目錄查找 package.json 文件,取出文件中定義的 main 屬性 ("main":"lib/hello.js") 描述的入口文件進(jìn)行加載墩朦,也沒加載到坯认,則會(huì)拋出默認(rèn)錯(cuò)誤: Error: Cannot find module 'lib/hello.js'

  • node_modules 目錄加載:對(duì)于系統(tǒng)模塊、路徑文件模塊都找不到氓涣,Node.js 會(huì)從當(dāng)前模塊的父目錄進(jìn)行查找牛哺,直到系統(tǒng)的根目錄

image

require 模塊加載時(shí)序圖

模塊緩存在哪

上面講解了模塊的加載機(jī)制,中間有提到模塊初次加載之后會(huì)緩存起來劳吠,有沒有疑問引润,模塊緩存在哪里?

Node.js 提供了 require.cache API 查看已緩存的模塊痒玩,返回值為對(duì)象椰拒,為了驗(yàn)證,這里做一個(gè)簡(jiǎn)單的測(cè)試凰荚,如下所示:

新建 test-module.js 文件

這里我導(dǎo)出一個(gè)變量和一個(gè)方法

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

新建 test.js 文件

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

在這個(gè)文件里加載 test-module.js 文件,在之后打印下 require.cache 看下里面返回的是什么褒脯?看到以下結(jié)果應(yīng)該就很清晰了便瑟,模塊的文件名、地址番川、導(dǎo)出數(shù)據(jù)都很清楚到涂。

image

模塊循環(huán)引用

問題1

假設(shè)有 a.js、b.js 兩個(gè)模塊相互引用颁督,會(huì)有什么問題践啄?是否為陷入死循環(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 中是否會(huì)被打映劣屿讽?

控制臺(tái)執(zhí)行 node a.js,查看輸出結(jié)果:

a模塊start
b模塊start
undeclaredVariable: a模塊未聲明變量
b模塊加載完畢: a.test值: 1
a模塊加載完畢: b.test值: 2

問題1吠裆,啟動(dòng) a.js 的時(shí)候伐谈,會(huì)加載 b.js,那么在 b.js 中又加載了 a.js试疙,但是此時(shí) a.js 模塊還沒有執(zhí)行完诵棵,返回的是一個(gè) a.js 模塊的 exports 對(duì)象 未完成的副本 給到 b.js 模塊(因此是不會(huì)陷入死循環(huán)的)。然后 b.js 完成加載之后將 exports 對(duì)象提供給了 a.js 模塊

問題2祝旷,因?yàn)?undeclaredVariable 是一個(gè)未聲明的變量履澳,也就是一個(gè)掛在全局的變量嘶窄,那么在其他地方當(dāng)然是可以拿到的。

在執(zhí)行代碼之前距贷,Node.js 會(huì)使用一個(gè)代碼封裝器進(jìn)行封裝柄冲,例如下面所示:

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

對(duì)象引用關(guān)系考察

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

exports 相當(dāng)于 module.exports 的快捷方式如下所示:

const exports = modules.exports;

但是要注意不能改變 exports 的指向储耐,我們可以通過 exports.test='a' 這樣來導(dǎo)出一個(gè)對(duì)象, 但是不能向下面示例直接賦值羊初,這樣會(huì)改變 exports 的指向

 // 錯(cuò)誤的寫法 將會(huì)得到 undefined
exports = {
     'a': 1,
     'b': 2
}
// 正確的寫法
modules.exports = {
     'a': 1,
     'b': 2
}

更好的理解之間的關(guān)系,可以參考 JavaScript 中的對(duì)象引用 https://www.nodejs.red/#/javascript/object?

文章來源

轉(zhuǎn)載自 Nodejs技術(shù)棧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末什湘,一起剝皮案震驚了整個(gè)濱河市长赞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闽撤,老刑警劉巖得哆,帶你破解...
    沈念sama閱讀 216,591評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異哟旗,居然都是意外死亡贩据,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門闸餐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饱亮,“玉大人,你說我怎么就攤上這事舍沙〗希” “怎么了?”我有些...
    開封第一講書人閱讀 162,823評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵拂铡,是天一觀的道長(zhǎng)壹无。 經(jīng)常有香客問我,道長(zhǎng)感帅,這世上最難降的妖魔是什么斗锭? 我笑而不...
    開封第一講書人閱讀 58,204評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮失球,結(jié)果婚禮上岖是,老公的妹妹穿的比我還像新娘。我一直安慰自己实苞,他們只是感情好璧微,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著硬梁,像睡著了一般前硫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荧止,一...
    開封第一講書人閱讀 51,190評(píng)論 1 299
  • 那天屹电,我揣著相機(jī)與錄音阶剑,去河邊找鬼。 笑死危号,一個(gè)胖子當(dāng)著我的面吹牛牧愁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播外莲,決...
    沈念sama閱讀 40,078評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼猪半,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了偷线?” 一聲冷哼從身側(cè)響起磨确,我...
    開封第一講書人閱讀 38,923評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎声邦,沒想到半個(gè)月后乏奥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,334評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亥曹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評(píng)論 2 333
  • 正文 我和宋清朗相戀三年邓了,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媳瞪。...
    茶點(diǎn)故事閱讀 39,727評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡骗炉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛇受,到底是詐尸還是另有隱情句葵,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評(píng)論 5 343
  • 正文 年R本政府宣布龙巨,位于F島的核電站,受9級(jí)特大地震影響熊响,放射性物質(zhì)發(fā)生泄漏旨别。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評(píng)論 3 326
  • 文/蒙蒙 一汗茄、第九天 我趴在偏房一處隱蔽的房頂上張望秸弛。 院中可真熱鬧,春花似錦洪碳、人聲如沸递览。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽绞铃。三九已至,卻和暖如春嫂侍,著一層夾襖步出監(jiān)牢的瞬間儿捧,已是汗流浹背荚坞。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菲盾,地道東北人颓影。 一個(gè)月前我還...
    沈念sama閱讀 47,734評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像懒鉴,于是被迫代替她去往敵國和親诡挂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容