# 模塊機(jī)制
node采用模塊化結(jié)構(gòu)冯丙,按照CommonJS規(guī)范定義和使用模塊,模塊與文件是一一對(duì)應(yīng)關(guān)系援雇,即加載一個(gè)模塊裕循,實(shí)際上就是加載對(duì)應(yīng)的一個(gè)模塊文件
node 的基礎(chǔ)中毫無疑問的應(yīng)該是有關(guān)于模塊機(jī)制的方面的,也即require這個(gè)內(nèi)置功能的一些原理娇昙,關(guān)于模塊互相引用的推薦先好好讀讀官方文檔尺迂,在
這里就不給大家詳細(xì)解釋了
# 熱更新
從面試官的角度看, 熱更新 是很多程序常見的問題,對(duì)客戶端而言噪裕,熱更新意味著不用換包蹲盘,當(dāng)然也包含著md5校驗(yàn)/差異更新等。復(fù)雜問題:
對(duì)服務(wù)端而言膳音,熱更新意味著服務(wù)不用重啟召衔,這樣可用性較高。
在node中做熱更新代碼祭陷,牽扯到的知識(shí)點(diǎn)可能主要是require會(huì)有一個(gè)cache苍凛,有這個(gè)cache在,即使你更新了 .js文件兵志,在
代碼中再次require還是會(huì)拿到之前的編譯好緩存在v8內(nèi)存(code space)中的舊代碼毫深,但是如果只是淡出的清除掉require中的cache,再次
require確實(shí)能拿到新的代碼毒姨,但是這時(shí)候很容易碰到各地維持舊的引用依舊跑的舊的代碼的問題哑蔫。
不過熱更新json之類的配置文件的話,還是可以簡單的實(shí)現(xiàn)弧呐,更新require的cache就行闸迷,不會(huì)
有持有舊引用的問題,但是舊的引用一直被持有很容易出現(xiàn)內(nèi)從泄露俘枫,而要熱更新配置的話腥沽,為什么不存數(shù)據(jù)庫?或者使用zookeeper之類
的服務(wù)鸠蚪?通過更新文件還要在發(fā)布一次今阳,但是存數(shù)據(jù)庫直接寫個(gè)接口配個(gè)頁面多爽
# 上下文
對(duì)于node.js而言,正常情況下只有一個(gè)上下文茅信,甚至于內(nèi)置的很多方面例如 require的實(shí)現(xiàn)只是在啟動(dòng)的時(shí)候運(yùn)行了內(nèi)置的函數(shù)
每個(gè)單獨(dú)的.js文件并不意味著單獨(dú)的上下文盾舌,在某個(gè).js文件中污染了全局的作用域一樣能影響到其它的地方
而目前的node.js將VM的接口暴露了出來,可以讓你自己創(chuàng)建一個(gè)新的js上下文蘸鲸,這一點(diǎn)上跟前端還是區(qū)別挺大的妖谴,在執(zhí)行外部代碼的
時(shí)候,通過創(chuàng)建新的上下文沙盒(sandbox)可以避免上下文被污染
'use strict';
const vm = require('vm');
let code =
`(function(require) {
const http = require('http');
http.createServer( (request, response) => {
response.writeHead(200, {'Content-Type': 'text/plain'});
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`;
vm.runInThisContext(code)(require);
問題:既然可以通過新的上下文來避免污染酌摇,那么為什么 node.js不給每一個(gè).js文件以獨(dú)立的上下文來避免作用域被污染
問題其實(shí)有點(diǎn)下套膝舅,其實(shí)Node有給每個(gè)js文件獨(dú)立的上下文,但是這避免不了全局的作用域污染窑多,實(shí)際上這是為了功能的妥協(xié)仍稀。
Node.js 模塊正常情況對(duì)作用域不會(huì)造成污染,意外創(chuàng)建全局變量是一種例外埂息,可以采用嚴(yán)格模式來避免
# 包管理
------------------------------------------------------
1. a.js和b.js兩個(gè)文件互相require是否會(huì)死循環(huán)技潘?雙方是否能導(dǎo)出變量遥巴?如何從設(shè)計(jì)上避免這種問題?
答:不會(huì)崭篡,先執(zhí)行的導(dǎo)出空對(duì)象挪哄,通過導(dǎo)出工廠函數(shù)讓對(duì)方從函數(shù)去拿比較好避免。
模塊在導(dǎo)出的只是 var module = { exports: {}};中的exports琉闪,以從a.js啟動(dòng)為例迹炼,a.js還沒執(zhí)行完exports就是{} 在b.js的開頭拿到的就是 {} 而已。
另外還有非车弑校基礎(chǔ)和常見的問題斯入,比如:module.exports和exports的區(qū)別這里也能一并解決了,exports只是module.exports的一個(gè)引用
2. 如果a.js require了b.js蛀蜜,那么在b中定義的全局變量t=11能否在a中直接打印出來刻两?
答:每個(gè).js能獨(dú)立一個(gè)環(huán)境只是因?yàn)閚ode幫你在外層包了一圈自執(zhí)行,所以你使用 t=11 定義全局變量在其它地方當(dāng)然能拿到滴某,情況如下:
//b.js
(function (exports,require,module,__filename,__dirname){
t=11;
})();
//a.js
(function (exports,require,nodule,__filename,__dirname){
console.log(t);//11
})();
3. 如何在不重啟node進(jìn)程的情況下熱更新一個(gè)js/json文件磅摹?這個(gè)問題本身是否有問題?
答:可以清除掉require的緩存? 重新require(),視具體情況還可以用VM模塊重新執(zhí)行霎奢。當(dāng)然這個(gè)問題可能是典型的X-Y Problem户誓,使用js實(shí)現(xiàn)熱更新很容易碰到v8優(yōu)化之后各地拿到緩存的引用導(dǎo)致熱更新js沒意義,當(dāng)然熱更新json還是可以簡單一點(diǎn)比如用讀取文件的方式來熱更新幕侠,但是這樣也不如從redis之類的數(shù)據(jù)庫中讀取比較合理
4. 比較 AMD, CMD, CommonJS 三者的區(qū)別帝美?
AMD,CMD,CommonJS是目前最常用的三種模塊化書寫規(guī)范。
commonjs是用在服務(wù)器端的晤硕,同步的悼潭,如nodejs
amd, cmd是用在瀏覽器端的,異步的舞箍,如requirejs和seajs
其中舰褪,amd先提出,cmd是根據(jù)commonjs和amd基礎(chǔ)上提出的创译。
根據(jù)CommonJS規(guī)范抵知,一個(gè)單獨(dú)的文件就是一個(gè)模塊。加載模塊使用require方法软族,該方法讀取一個(gè)文件并執(zhí)行,最后返回文件內(nèi)部的exports對(duì)象残制。 CommonJS 加載模塊是同步的立砸,所以只有加載完成才能執(zhí)行后面的操作。像Node.js主要用于服務(wù)器的編程初茶,加載的模塊文件一般都已經(jīng)存在本地硬盤颗祝,所以加載起來比較快,不用考慮異步加載的方式,所以CommonJS規(guī)范比較適用螺戳。但如果是瀏覽器環(huán)境搁宾,要從服務(wù)器加載模塊,這是就必須采用異步模式倔幼。所以就有了 AMD? CMD 解決方案盖腿。
AMD 是 RequireJS 在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出
AMD異步加載模塊。它的模塊支持對(duì)象 函數(shù) 構(gòu)造器 字符串 JSON等各種類型的模塊损同。
適用AMD規(guī)范適用define方法定義模塊翩腐。
CMD是SeaJS 在推廣過程中對(duì)模塊定義的規(guī)范化產(chǎn)出
CMD和AMD的區(qū)別有以下幾點(diǎn):
1.對(duì)于依賴的模塊AMD是提前執(zhí)行,CMD是延遲執(zhí)行膏燃。不過RequireJS從2.0開始茂卦,也改成可以延遲執(zhí)行(根據(jù)寫法不同,處理方式不通過)组哩。
2.CMD推崇依賴就近等龙,AMD推崇依賴前置。
3.AMD的api默認(rèn)是一個(gè)當(dāng)多個(gè)用伶贰,CMD嚴(yán)格的區(qū)分推崇職責(zé)單一蛛砰。例如:AMD里require分全局的和局部的。CMD里面沒有全局的 require,提供 seajs.use()來實(shí)現(xiàn)模塊系統(tǒng)的加載啟動(dòng)幕袱。CMD里每個(gè)API都簡單純粹暴备。
5. 關(guān)于 node 中 require 的實(shí)現(xiàn)原理等
答:require原生入口代碼里面調(diào)用了__load方法用于加載文件,繼續(xù)看__load方法原生代碼里面調(diào)用了_resolveFilename方法们豌,顧名思義涯捻,這應(yīng)該是一個(gè)解析需要require的文件名的方法,繼續(xù)看_resolveFilename方法中又調(diào)用了_findPath方法望迎。
可以看到障癌,這里完整的顯示了node是如何根據(jù)require傳入的名稱來定位具體的文件的,他們的順序依次是:
1辩尊、先從緩存中讀取涛浙,如果沒有則繼續(xù)往下
2、判斷需要模塊路徑是否以/結(jié)尾摄欲,如果不是轿亮,則要判斷
a. 檢查是否是一個(gè)文件,如果是胸墙,則轉(zhuǎn)換為真實(shí)路徑
b. 否則如果是一個(gè)目錄我注,則調(diào)用tryPackage方法讀取該目錄下的package.json文件,把里面的main屬性設(shè)置為filename
c. 如果沒有讀到路徑上的文件迟隅,則通過tryExtensions嘗試在該路徑后依次加上.js但骨,.json和.node后綴励七,判斷是否存在,若存在則返回加上后綴后的路徑
3奔缠、如果依然不存在掠抬,則同樣調(diào)用tryPackage方法讀取該目錄下的package.json文件,把里面的main屬性設(shè)置為filename
4校哎、如果依然不存在两波,則嘗試在該路徑后依次加上index.js,index.json和index.node贬蛙,判斷是否存在雨女,若存在則返回拼接后的路徑。
5阳准、若解析成功氛堕,則把解析得到的文件名cache起來,下次require就不用再次解析了野蝇,否則若解析失敗讼稚,則返回false
------------------------
# Promise
Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案--回調(diào)函數(shù)和事件--更合理和更前大
所謂Promise绕沈,簡單說就是一個(gè)容器锐想,里面保存著某個(gè)未來才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果,從語法上說乍狐,Promise是一個(gè)對(duì)象赠摇,從他可以獲取異步操作的消息,Promise提供統(tǒng)一的API浅蚪,各種異步操作都可以用同樣的方法進(jìn)行處理
有了Promise對(duì)象藕帜,就可以將異步操作以同步操作的方式表達(dá)出來,避免層層嵌套的回調(diào)函數(shù)
Promise對(duì)象的兩個(gè)特點(diǎn):
1.對(duì)象的狀態(tài)不受外界影響
Promise對(duì)象代表一個(gè)異步操作惜傲,有三種狀態(tài):pending(進(jìn)行中)洽故,fulfilled(已成功),rejected(已失敗)盗誊,只有異步操作的結(jié)果时甚,可以決定當(dāng)前是哪一種狀態(tài),任何其它操作都無法改變這個(gè)狀態(tài)哈踱。這也是Promise這個(gè)名字的由來荒适,它的英語意思就是“承諾”,表示其它手段無法改變
2.一旦狀態(tài)改變,就不會(huì)再變开镣,任何時(shí)候都可以得到這個(gè)結(jié)果
resolved(已定型)
promise對(duì)象的狀態(tài)改變只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected吻贿,只要這兩種情況發(fā)生,狀態(tài)就凝固了哑子,不會(huì)再變了舅列,會(huì)一直保持這個(gè)結(jié)果,這時(shí)稱為已定型
缺點(diǎn):
首先卧蜓,無法取消Promise帐要,一旦新建它就會(huì)立即執(zhí)行,無法中途取消弥奸。
其次榨惠,如果不設(shè)置回調(diào)函數(shù),promise內(nèi)部拋出的錯(cuò)誤盛霎,不會(huì)反應(yīng)到外部
當(dāng)處于pending狀態(tài)時(shí)赠橙,無法得知目前進(jìn)展到哪一個(gè)階段
# Events(事件)
# Timers(定時(shí)器)
# 阻塞/異步
# 并行/并發(fā)
1. Promise 中 .then的第二個(gè)參數(shù)與.catch 有什么區(qū)別?
沒什么多大的區(qū)別愤炸,全都是用來處理錯(cuò)誤函數(shù)的
詳情可參考:http://es6.ruanyifeng.com/#docs/promise
2. Eventemitter的emit是同步還是異步
Node.js 中 Eventemitter 的 emit 是同步的
按監(jiān)聽器的注冊(cè)順序期揪,同步地調(diào)用每個(gè)注冊(cè)到名為 eventName 事件的監(jiān)聽器,并傳入提供的參數(shù)规个。
如果事件有監(jiān)聽器凤薛,則返回 true ,否則返回 false诞仓。
3. 如何判斷接口是否異步缤苫?是否只要有回調(diào)函數(shù)就是異步?
異步:發(fā)出指令墅拭,然后去做別的事情了活玲,所有操作完成后再執(zhí)行回調(diào)
異步I/O:異步的發(fā)出I/O請(qǐng)求
看文檔
console.log 打印看看
看是否有 IO 操作
單純使用回調(diào)函數(shù)并不會(huì)異步, IO 操作才可能會(huì)異步, 除此之外還有使用 setTimeout 等方式實(shí)現(xiàn)異步.
4. nextTick,setTimeout以及setImmediate三者的區(qū)別?
5. 如何實(shí)現(xiàn)一個(gè)sleep函數(shù)谍婉?
function sleep(ms) {
var start = Date.now(), expire = start + ms;
while (Date.now() < expire) ;
return;
}
6. 如何實(shí)現(xiàn)一個(gè)異步的reduce舒憾?(在Es5中出現(xiàn)的數(shù)組方法)
需要了解 reduce 的情況, 是第 n 個(gè)與 n+1 的結(jié)果異步處理完之后, 在用新的結(jié)果與第 n+2 個(gè)元素繼續(xù)依次異步下去
# 進(jìn)程
## Process(進(jìn)程)
## Child Processes(子進(jìn)程)
## Cluster(集群)
## 進(jìn)程間通信
## 守護(hù)進(jìn)程
1. 進(jìn)程的當(dāng)前工作目錄是什么?有什么作用屡萤?
2. child_process.fork與POSIX的fork有什么區(qū)別珍剑?
cluster是常見的node利用多核的方法,它是基于child_process.fork()實(shí)現(xiàn)的,所以cluster產(chǎn)生的進(jìn)程之間是通過IPC來通信的死陆,并且它也沒有拷貝父進(jìn)程的空間招拙,而是通過加入cluster.isMaster這個(gè)標(biāo)識(shí),來區(qū)分父進(jìn)程以及子進(jìn)程措译,達(dá)到類似POSIX的fork效果
3. 父進(jìn)程或子進(jìn)程的死亡是否回影響對(duì)方别凤?什么是孤兒進(jìn)程?
子進(jìn)程的死亡不會(huì)影響父進(jìn)程领虹,不過子進(jìn)程死亡時(shí)(線程組的最后一個(gè)線程规哪,通常是‘領(lǐng)頭線程死亡時(shí)’)會(huì)向他的父進(jìn)程發(fā)送死亡信號(hào)。反之父進(jìn)程死亡塌衰,一般情況下子進(jìn)程也會(huì)隨之死亡诉稍,但如果此時(shí)子進(jìn)程處于可運(yùn)狀態(tài)蝠嘉,僵死狀態(tài)等等的話,子進(jìn)程將被進(jìn)程1(init進(jìn)程)收養(yǎng)杯巨,從而成為孤兒進(jìn)程蚤告。
子進(jìn)程死亡的時(shí)候(處于‘終止?fàn)顟B(tài)’),父進(jìn)程沒有及時(shí)調(diào)用wait()或waitpid()來返回死亡進(jìn)程的相關(guān)信息,此時(shí)子進(jìn)程還有一個(gè)PCB殘留在進(jìn)程表中服爷,被稱為僵尸進(jìn)程杜恰。
4. cluster是如何保證負(fù)載均衡的?
5. 什么是守護(hù)進(jìn)程仍源?如何實(shí)現(xiàn)守護(hù)進(jìn)程心褐?
守護(hù)進(jìn)程,是服務(wù)端方面一個(gè)很基礎(chǔ)的概念笼踩。很多人只知道通過pm2之類的工具可以將進(jìn)程以守護(hù)進(jìn)程的方式啟動(dòng)逗爹,卻不了解什么是守護(hù)進(jìn)程,為什么要使用守護(hù)進(jìn)程
普通的進(jìn)程戳表,在用戶退出終端之后就會(huì)直接關(guān)閉桶至,通過&啟動(dòng)到后臺(tái)的進(jìn)程,之后會(huì)由于會(huì)話(session組)被回收而終止進(jìn)程匾旭,守護(hù)進(jìn)程是不依賴終端(tty)的進(jìn)程镣屹,不會(huì)因?yàn)橛脩敉顺鼋K端而停止運(yùn)行的進(jìn)程
# IO
# Buffer
# String Decoder(字符串解碼)
# Stream (流)
# Console (控制臺(tái))
# File System (文件系統(tǒng))
# Readline
# REPL
1.Buffer一般處理什么數(shù)據(jù)?其長度能否動(dòng)態(tài)變化价涝?
Buffer是node.js中用于處理二進(jìn)制數(shù)據(jù)的類女蜈,其中與IO相關(guān)的操作(網(wǎng)絡(luò)/文件等)均基于Buffer,Buffer類的實(shí)例非常類似整數(shù)數(shù)組
但其大小是固定不變的色瘩,并且其內(nèi)存在V8堆棧外分配原始內(nèi)存空間伪窖。Buffer類的實(shí)例創(chuàng)建之后,其所占用的內(nèi)存大小不能再進(jìn)行調(diào)整
2.Stream的highWaterMark與drain事件是什么居兆?二者之間的關(guān)系是覆山?
3.Stream的pipe的作用是?在pipe的過程中數(shù)據(jù)是引用傳遞還是拷貝傳遞泥栖?
將一個(gè)可寫流附到可讀流上簇宽,同時(shí)將可寫切換到流模式,并把所有數(shù)據(jù)推給可寫流吧享,在pipe傳遞數(shù)據(jù)的過程中魏割,objectMode是傳遞引用,非objectMode則是拷貝一份數(shù)據(jù)傳遞下去
4.什么是文件描述符钢颂?輸入流钞它,輸出流,錯(cuò)誤流是什么?
5.console.log是同步還是異步遭垛?如何實(shí)現(xiàn)一個(gè)console.log?
console.log正常情況下是異步的尼桶,除非你使用new Console(stdout[,stderr])指定了一個(gè)文件的目的地
let print = (str) =>? process.stdout.write(str + '\n')
print('hello world')
6.如何同步的獲取用戶的輸入?
node中耻卡,獲取用戶的輸入其實(shí)就是讀取node進(jìn)程中的輸入流的數(shù)據(jù)
而要同步讀取疯汁,則是不用異步的read接口,而是用同步的readSync接口去讀取stdin的數(shù)據(jù)可實(shí)現(xiàn)
7.Readline是如何實(shí)現(xiàn)的卵酪?
readline模塊提供了一個(gè)用于從Readble的stream中一次讀取一行的接口,當(dāng)然你也可以用于讀取文件或net,http,stream.
實(shí)現(xiàn)上谤碳,readline在讀取TTY的數(shù)據(jù)時(shí)溃卡,是通過input.on('keypress','onkeypress')時(shí)發(fā)現(xiàn)用戶按下了回車鍵來判斷是新的line的,而讀取一般的stream時(shí)蜒简,則是通過緩存數(shù)據(jù)然后用正則,test來判斷是否為new line
8.REPL
Read--Eval--Print--Loop(交互式解釋器)
repl模塊提供了一種‘讀取--求值-輸出-循環(huán)的實(shí)現(xiàn)’它可以作為一個(gè)獨(dú)立的程序或嵌入到其它應(yīng)用中
9.Cluster模塊
node默認(rèn)單線程進(jìn)行瘸羡,對(duì)于32位系統(tǒng)最高可以使用512MB內(nèi)存,對(duì)于64位最高可以使用1GB內(nèi)存搓茬。對(duì)于多核CPU的計(jì)算機(jī)來說犹赖,這樣做效率很低,因?yàn)橹挥幸粋€(gè)核在運(yùn)行卷仑,其它核都在閑置峻村。cluster模塊就是為解決這個(gè)問題而提出的
Cluster模塊允許建立一個(gè)主進(jìn)程和若干個(gè)worker進(jìn)程,由主進(jìn)程監(jiān)控和協(xié)調(diào)worker進(jìn)程的運(yùn)行锡凝,worker之間采用進(jìn)程間通信交換信息粘昨,cluster模塊內(nèi)置一個(gè)負(fù)載均衡器,采用Round-robin算法協(xié)調(diào)各個(gè)worker進(jìn)程之間的負(fù)載窜锯,運(yùn)行時(shí)张肾,所有新建立的鏈接都有主進(jìn)程完成,然后主進(jìn)程再把TCP連接分配給指定的worker進(jìn)程
10. Events模塊
回調(diào)函數(shù)模式讓node可以處理異步操作锚扎,但是洞翩,為了適應(yīng)回調(diào)函數(shù),異步操作只能有兩個(gè)狀態(tài):開始和結(jié)束坯癣。對(duì)于那些多狀態(tài)的異步操作炸庞,回調(diào)函數(shù)就會(huì)無法處理,你不得不將異步操作拆開助币,分成多個(gè)階段浪听,每個(gè)階段結(jié)束時(shí),調(diào)用下一個(gè)回調(diào)函數(shù)眉菱。
為了解決這個(gè)問題迹栓,node提供Event Emitter接口。通過事件俭缓,解決多狀態(tài)異步操作的響應(yīng)問題
11. child_process模塊
用于新建子進(jìn)程克伊,子進(jìn)程的運(yùn)行結(jié)果儲(chǔ)存在系統(tǒng)緩存之中(最大200KB)酥郭,等到子進(jìn)程運(yùn)行結(jié)束后,主進(jìn)程在調(diào)用回調(diào)函數(shù)讀取子進(jìn)程的運(yùn)行結(jié)果
#小題型
1. 什么是error-first回調(diào)模式愿吹?
錯(cuò)誤優(yōu)先處理回調(diào)函數(shù)不从,應(yīng)用error-first回調(diào)模式是為了更好的進(jìn)行錯(cuò)誤和數(shù)據(jù)的傳遞,第一個(gè)參數(shù)保留給一個(gè)錯(cuò)誤error對(duì)象犁跪,一旦出現(xiàn)錯(cuò)誤椿息,錯(cuò)誤將通過第一個(gè)參數(shù)error返回。其余的參數(shù)將用作數(shù)據(jù)的傳遞坷衍。
2. 如何避免回調(diào)地獄寝优?
模塊化設(shè)計(jì):將回調(diào)函數(shù)拆分成幾個(gè)獨(dú)立的函數(shù)
使用流程控制庫,比如async
組合使用generators和Promises
使用async/await函數(shù)
3. 什么是Promises枫耳?
在 then 后沒有 catch 乏矾,沒有捕捉異常。這樣做會(huì)造成故障沉默迁杨,不會(huì)拋出異常钻心。
如果你調(diào)試一個(gè)巨大的代碼庫,并且比不知道哪個(gè) Promise 函數(shù)有潛在的問題, 你可以使用
unhandledRejection 這個(gè)工具铅协。它將會(huì)打印出所有未處理的 reject 狀態(tài)的 Promise捷沸。
4. 什么工具統(tǒng)一團(tuán)隊(duì)的代碼風(fēng)格?為什么統(tǒng)一的代碼風(fēng)格很重要警医?
ESLint和Standard可以用來統(tǒng)一代碼
因?yàn)閳F(tuán)隊(duì)協(xié)作時(shí)亿胸,代碼風(fēng)格一致,團(tuán)隊(duì)成員可以更輕松的構(gòu)建項(xiàng)目预皇,可以通過靜態(tài)分析排除代碼問題
5. 什么時(shí)候用npm侈玄?什么時(shí)候應(yīng)當(dāng)用yarn?
6. 什么是樁代碼(stub)吟温?請(qǐng)描述一下應(yīng)用場景序仙?
樁代碼就是在某些組件或模塊中,模擬某些功能的代碼鲁豪。樁代碼的作用是占位潘悼,讓代碼在測(cè)試過程中順利進(jìn)行,測(cè)試時(shí)爬橡,stub可以為函數(shù)調(diào)用返回模擬的結(jié)果治唤,比如,當(dāng)我們寫文件時(shí)糙申,實(shí)際上并不需要真正去寫
7. 什么是測(cè)試金字塔宾添?請(qǐng)舉例說明?
測(cè)試金字塔反映了單元測(cè)試,集成測(cè)試缕陕,端到端測(cè)試在測(cè)試中占的比例
8. 你最欣賞的HTTP框架是什么粱锐?為什么?
9. 如何保證你的cookie安全扛邑?如何阻止XSS攻擊怜浅?
XSS攻擊是指攻擊者向html頁面里面插入惡意JavaScript代碼
HttpOnly 這個(gè)屬性幫助防止跨站腳本攻擊,它禁止通過JavaScript訪問cookie
為了防止攻擊蔬崩,可以對(duì)HTTP header里的set-cookie進(jìn)行處理set-cookie:sid=;HttpOnly.
如果使用express框架恶座,可以使用express-cookie session,它會(huì)默認(rèn)做出上述防止XSS攻擊的設(shè)置
10. 如何確認(rèn)項(xiàng)目的相關(guān)依賴安全舱殿?
自動(dòng)的更新你的依賴: npm outdated
NSP
11. 什么時(shí)候應(yīng)該在后臺(tái)進(jìn)程中使用消息服務(wù)奥裸,怎樣處理工作想成的任務(wù)/怎么給worker安排任務(wù)?
消息隊(duì)列適用于后臺(tái)數(shù)據(jù)傳輸服務(wù)沪袭,比如發(fā)送郵件和數(shù)據(jù)圖像處理
消息隊(duì)列有很多解決方案,比如RabbitMQQ
12. 這段代碼有什么問題樟氢?
new? Promise((resolve,reject) => {
throw new Error('error')
})
.then(console.log)
then之后沒有catch冈绊。這樣的話,錯(cuò)誤會(huì)被忽略埠啃,會(huì)造成故障沉默
后邊跟上 .catch(console.error)
調(diào)試一個(gè)大型項(xiàng)目時(shí)死宣,可以使用監(jiān)控unhandleRejection事件來捕獲所有未處理的Promise錯(cuò)誤
process.on('unhandledRejection',(err) => {
console.log(err)
})
13. 這段代碼輸出什么?
Promise.resolve(1)
.then((x) => x + 1)
.then((x) => { throw new Error('My Error') })
.catch(() => 1)
.then((x) => x + 1)
.then((x) => console.log(x))
.catch(console.error)
答案是2碴开,逐行解釋如下:
1.創(chuàng)建promise毅该,resolve的值為1
2.x 的值為1,加1 返回2
3.x 的值為2潦牛,但是沒有用到眶掌,拋出一個(gè)錯(cuò)誤
4.捕獲錯(cuò)誤,但是沒有處理巴碗,返回1
5.x 的值為1朴爬,加1 返回2
6.x 的值為2,打印為2
7.不會(huì)執(zhí)行橡淆,因?yàn)闆]有錯(cuò)誤拋出
14.為什么要用node召噩?
簡單強(qiáng)大,輕量可擴(kuò)展
簡單體現(xiàn)在node使用JavaScript逸爵,json來進(jìn)行編碼具滴,人人都會(huì)
強(qiáng)大體現(xiàn)在非阻塞IO,可以適應(yīng)分塊傳輸數(shù)據(jù)师倔,較慢的網(wǎng)絡(luò)環(huán)境构韵,尤其擅長高并發(fā)訪問。
輕量體現(xiàn)在node本身既是代碼,又是服務(wù)器贞绳,前后端使用統(tǒng)一語言谷醉。
可擴(kuò)展體現(xiàn)在可以輕松應(yīng)對(duì)多實(shí)例,多服務(wù)架構(gòu)冈闭,同時(shí)有海量的第三方應(yīng)用組件
node的優(yōu)點(diǎn):I/O密集型處理是node的強(qiáng)項(xiàng)俱尼,因?yàn)閚ode的I/O請(qǐng)求都是異步的
node的缺點(diǎn):不擅長CPU密集型的操作
CPU密集型操作(復(fù)雜的運(yùn)算,圖片的操作)
15. node的架構(gòu)是什么樣子的萎攒?
主要分為三層遇八,應(yīng)用app >> v8及node內(nèi)置架構(gòu) >> 操作系統(tǒng)
v8是node運(yùn)行的環(huán)境,可以理解為node虛擬機(jī)
node內(nèi)置架構(gòu)又可分為三層:
核心模塊(javascript實(shí)現(xiàn)) >> c++綁定 >> libuv + CAes + http
16. node有哪些核心模塊耍休?
EventEmitter刃永,Stream,F(xiàn)S羊精,Net和全局對(duì)象
17. node在企業(yè)中常用到斯够?
靜態(tài)資源服務(wù)器,代碼本地構(gòu)建喧锦,單元測(cè)試读规,UI測(cè)試
工作中應(yīng)該注意:精確版本號(hào),測(cè)試燃少,使用debug束亏,保持代碼精簡
多請(qǐng)教,保持獨(dú)立思考阵具,使用現(xiàn)有的庫碍遍,保持簡單
良好的文檔,配置文件阳液,使用pm2
libuv:提供異步功能的C庫怕敬。它在運(yùn)行是負(fù)責(zé)一個(gè)事件循環(huán)(Event loop),一個(gè)線程池,文件系統(tǒng)I/O,DNS相關(guān)和網(wǎng)絡(luò)I/O,以及其它一些功能
18. node異常處理
node是單線程運(yùn)行環(huán)境趁舀,一旦拋出的異常沒有被捕獲就會(huì)引起整個(gè)進(jìn)程的崩潰赖捌。
使用throw語句拋出一個(gè)錯(cuò)誤對(duì)象,即拋出異常
將錯(cuò)誤對(duì)象傳遞給回調(diào)函數(shù)矮烹,由回調(diào)函數(shù)負(fù)責(zé)發(fā)出錯(cuò)誤
通過EventEmitter接口越庇,發(fā)出一個(gè)error事件
try...catch結(jié)構(gòu)無法捕獲異步運(yùn)行的代碼拋出的錯(cuò)誤
19. node關(guān)于高并發(fā)問題?
原理:非阻塞事件驅(qū)動(dòng)實(shí)現(xiàn)異步開發(fā)奉狈,通過事件驅(qū)動(dòng)的I/O來操作完成跨平臺(tái)數(shù)據(jù)密集型實(shí)時(shí)應(yīng)用
傳統(tǒng)的server每個(gè)請(qǐng)求生成一個(gè)線程卤唉,node是一個(gè)單線程的,使用libuv保持?jǐn)?shù)萬并發(fā)
libuv原理:
c語言編寫的基礎(chǔ)庫實(shí)現(xiàn)主循環(huán)仁期,文件桑驱,網(wǎng)絡(luò)即可
libuv的改進(jìn):
回傳上下文信息
其它線程不能訪問缺省主循環(huán)竭恬,loop不支持多線程
數(shù)據(jù)庫高并發(fā)實(shí)現(xiàn)
var proxy = new EventProxy();
var status = "ready";
var select = function(callback){
proxy.once("selected",callback);
if(status == "ready"){
status = "pending";
db.select("SQL", function(results){
proxy.emit("selected",results);
status = "ready";
});
}
20. 什么是異步?
異步就是發(fā)出操作指令(把回調(diào)函數(shù)加入異步隊(duì)列)熬的,然后就可以去做別的事情去了痊硕,所有操作完成后再執(zhí)行回調(diào)
異步I/O就是異步的發(fā)出I/O請(qǐng)求
雖然node可以異步發(fā)出I/O請(qǐng)求,但nodejs不支持多線程押框,為啥就可以支持高并發(fā)呢岔绸?
因?yàn)閚odejs的I/O操作,底層是開啟了多線程的
當(dāng)同時(shí)有多個(gè)I/O請(qǐng)求時(shí)橡伞,主線程會(huì)創(chuàng)建多個(gè)eio線程盒揉,以提高IO請(qǐng)求的處理速度
雖然nodeJS的IO操作開啟了多線程,但是所有線程都是基于主線程開啟的只能跑在一個(gè)進(jìn)程當(dāng)中還是不能充分利用CPU資源
pm2進(jìn)程管理器可以解決這個(gè)問題兑徘,pm2是一個(gè)帶有負(fù)載均衡功能的node應(yīng)用的進(jìn)程管理器
cluster模塊
21. 代碼可讀性維護(hù)改進(jìn)
async:async.waterfall([getcatalog,getaticle,getTigle])
promise的方法
koa寫法
es6寫法使用yield
22.優(yōu)化問題
前端優(yōu)化問題:移除iscorll,合并請(qǐng)求,tcp優(yōu)化刚盈,HTTP優(yōu)化,localstorate挂脑,html5離線緩存
api優(yōu)化:restfulapi,標(biāo)準(zhǔn)輸入輸出
ui優(yōu)化:使用同一的框架藕漱,前端組件化
異常處理:log監(jiān)控,避免大文件處理崭闲,retry處理