刷題是程序員面試準(zhǔn)備中至關(guān)重要的一件事,它直接決定你能否面試成功,或者在薪酬談判的時候是否具備主動優(yōu)勢。
JavaScript 很特殊,它幾乎在每個大型應(yīng)用程序中都扮演著至關(guān)重要的角色。如果你是一名JavaScript程序員,以下是一些可以幫助您探索真正重要內(nèi)容的問題。
對 JavaScript 應(yīng)用程序開發(fā)人員很重要的編程范式有哪些?JavaScript 是一種多范式語言,支持命令式、過程式編程以及OOP(面向?qū)ο缶幊蹋┖秃瘮?shù)式編程。JavaScript 支持具有原型繼承的OOP 。
(資料圖)
什么是函數(shù)式編程?函數(shù)式編程通過組合數(shù)學(xué)函數(shù)來生成程序,并避免共享狀態(tài)和可變數(shù)據(jù)。Lisp(于 1958 年指定)是最早支持函數(shù)式編程的語言之一,并且深受 lambda 演算的啟發(fā)。Lisp 和許多 Lisp 家族語言今天仍然被廣泛使用。
函數(shù)式編程是一種基于函數(shù)的編程范式,其中函數(shù)被視為一等公民。函數(shù)可以作為參數(shù)傳遞給其他函數(shù),也可以作為返回值返回。函數(shù)式編程鼓勵使用不可變數(shù)據(jù)和無副作用的函數(shù)。這意味著函數(shù)只依賴于它的輸入,并且不會更改任何外部狀態(tài)。在JavaScript中,函數(shù)式編程可以使用高階函數(shù)、閉包和遞歸等概念來實現(xiàn)。
經(jīng)典繼承和原型繼承有什么區(qū)別?類繼承:實例繼承自類(如藍(lán)圖——類的描述),并創(chuàng)建子類關(guān)系:層次類分類法。實例通常通過帶有“new”關(guān)鍵字的構(gòu)造函數(shù)實例化。類繼承可能會也可能不會使用ES6 中的class關(guān)鍵字。
原型繼承:實例直接從其他對象繼承。實例通常通過工廠函數(shù)或Object.create() 實例化。實例可以由許多不同的對象組成,允許簡單的選擇性繼承。
在 JavaScript 中,原型繼承比類繼承更簡單、更靈活。
函數(shù)式編程與面向?qū)ο缶幊痰膬?yōu)缺點是什么?OOP優(yōu)點:容易理解對象的基本概念,容易理解方法調(diào)用的含義。OOP 傾向于使用命令式風(fēng)格而不是聲明式風(fēng)格,它讀起來就像一組直接的指令供計算機遵循。
OOP 缺點: OOP 通常依賴于共享狀態(tài)。對象和行為通常在同一個實體上捆綁在一起,可以由任意數(shù)量的具有不確定順序的函數(shù)隨機訪問,這可能導(dǎo)致不良行為,例如競爭條件。
FP 的優(yōu)點:使用函數(shù)范式,程序員可以避免任何共享狀態(tài)或副作用,從而消除多個函數(shù)競爭相同資源而導(dǎo)致的錯誤。與 OOP 相比,借助無點風(fēng)格(又名默認(rèn)編程)等功能,功能往往會被徹底簡化并輕松重組為更普遍可重用的代碼。
FP 也傾向于支持聲明式和指稱式風(fēng)格,這些風(fēng)格不會詳細(xì)說明操作的分步說明,而是專注于做什么,讓底層函數(shù)負(fù)責(zé)如何操作。這為重構(gòu)和性能優(yōu)化留下了巨大的空間,甚至允許您用更高效的算法替換整個算法,而只需很少的代碼更改。(例如,memoize 或使用惰性求值代替急切求值。)
使用純函數(shù)的計算也很容易跨多個處理器或跨分布式計算集群進(jìn)行擴展,而不必?fù)?dān)心線程資源沖突、競爭條件等……
FP 缺點:過度利用 FP 特性(例如無點樣式和大型組合)可能會降低可讀性,因為生成的代碼通常更抽象地指定、更簡潔且更不具體。
與函數(shù)式編程相比,更多人熟悉OO和命令式編程,因此即使是函數(shù)式編程中的常見習(xí)語也會讓新團隊成員感到困惑。
FP 的學(xué)習(xí)曲線比 OOP 陡峭得多,因為 OOP 的廣泛流行使得 OOP 的語言和學(xué)習(xí)材料變得更具會話性,而 FP 的語言往往更加學(xué)術(shù)和正式。FP 概念經(jīng)常寫成關(guān)于使用 lambda 演算、代數(shù)和范疇論中的習(xí)語和符號,所有這些都需要在這些領(lǐng)域有先驗知識基礎(chǔ)才能理解。
什么時候經(jīng)典繼承是合適的選擇?答案是從不,或者幾乎從不。當(dāng)然永遠(yuǎn)不會超過一個級別。多級類層次結(jié)構(gòu)是一種反模式。
什么時候原型繼承是合適的選擇?原型繼承的類型不止一種:
委托(即原型鏈)。串聯(lián)(即 mixins,Object.assign())。函數(shù)式(不要與函數(shù)式編程混淆。用于為私有狀態(tài)/封裝創(chuàng)建閉包的函數(shù))。每種類型的原型繼承都有自己的一組用例,但它們在啟用組合方面同樣有用,組合創(chuàng)建了has-a或uses-a或can-do關(guān)系,而不是 is -a關(guān)系使用類繼承創(chuàng)建。
“對象組合優(yōu)先于類繼承”是什么意思?這意味著代碼重用應(yīng)該通過將更小的功能單元組裝到新對象中來實現(xiàn),而不是從類繼承和創(chuàng)建對象分類法。
換句話說,使用can-do、has-a或uses-a關(guān)系,而不是is-a關(guān)系。
什么是雙向數(shù)據(jù)綁定和單向數(shù)據(jù)流,它們有何不同?雙向數(shù)據(jù)綁定意味著 UI 字段動態(tài)綁定到模型數(shù)據(jù),這樣當(dāng) UI 字段更改時,模型數(shù)據(jù)也隨之更改,反之亦然。
數(shù)據(jù)流的一種方式意味著模型是唯一的事實來源。UI 中的更改會觸發(fā)消息,這些消息會向模型發(fā)出用戶意圖信號(或 React 中的“存儲”)。只有模型有權(quán)更改應(yīng)用程序的狀態(tài)。效果是數(shù)據(jù)總是單向流動,這樣更容易理解。
數(shù)據(jù)流的一種方式是確定性的,而雙向綁定會導(dǎo)致難以理解和理解的副作用。
單體架構(gòu)與微服務(wù)架構(gòu)的優(yōu)缺點是什么?單體架構(gòu)意味著您的應(yīng)用程序被編寫為一個內(nèi)聚的代碼單元,其組件旨在協(xié)同工作,共享相同的內(nèi)存空間和資源。
微服務(wù)架構(gòu)意味著您的應(yīng)用程序由許多較小的、獨立的應(yīng)用程序組成,這些應(yīng)用程序能夠在自己的內(nèi)存空間中運行并在可能的許多獨立機器上相互獨立地擴展。
整體式優(yōu)點:整體式架構(gòu)的主要優(yōu)點是大多數(shù)應(yīng)用程序通常具有大量橫切關(guān)注點,例如日志記錄、速率限制和安全功能(例如審計跟蹤和 DOS 保護(hù))。
當(dāng)一切都通過同一個應(yīng)用程序運行時,很容易將組件連接到那些橫切關(guān)注點。
還可能有性能優(yōu)勢,因為共享內(nèi)存訪問比進(jìn)程間通信 (IPC) 更快。
整體式缺點:隨著應(yīng)用程序的發(fā)展,整體式應(yīng)用程序服務(wù)往往會緊密耦合和糾纏在一起,因此很難為獨立擴展或代碼可維護(hù)性等目的隔離服務(wù)。
單體架構(gòu)也更難理解,因為可能存在依賴關(guān)系、副作用和魔法,當(dāng)您查看特定服務(wù)或控制器時,這些并不明顯。
微服務(wù)優(yōu)點:微服務(wù)架構(gòu)通常組織得更好,因為每個微服務(wù)都有一個非常具體的工作,并且不關(guān)心其他組件的工作。分離的服務(wù)也更容易重組和重新配置以服務(wù)于不同應(yīng)用程序的目的(例如,同時服務(wù)于 Web 客戶端和公共 API)。
它們還可以具有性能優(yōu)勢,具體取決于它們的組織方式,因為可以隔離熱門服務(wù)并獨立于應(yīng)用程序的其余部分?jǐn)U展它們。
微服務(wù)缺點:在構(gòu)建新的微服務(wù)架構(gòu)時,您可能會發(fā)現(xiàn)許多在設(shè)計時沒有預(yù)料到的橫切關(guān)注點。一個單一的應(yīng)用程序可以建立共享的魔法助手或中間件來處理這樣的橫切問題而不需要太多努力。
在微服務(wù)架構(gòu)中,您需要為每個橫切關(guān)注點承擔(dān)單獨模塊的開銷,或者將橫切關(guān)注點封裝在所有流量都經(jīng)過的另一個服務(wù)層中。
最終,即使是單體架構(gòu)也傾向于通過外部服務(wù)層路由流量以實現(xiàn)橫切關(guān)注點,但使用單體架構(gòu),可以延遲這項工作的成本,直到項目更加成熟。
微服務(wù)經(jīng)常部署在自己的虛擬機或容器上,導(dǎo)致 VM 爭論工作激增。這些任務(wù)經(jīng)常通過集裝箱車隊管理工具實現(xiàn)自動化。
什么是異步編程,為什么它在 JavaScript 中很重要?同步編程意味著,除了條件和函數(shù)調(diào)用,代碼從上到下按順序執(zhí)行,阻塞長時間運行的任務(wù),如網(wǎng)絡(luò)請求和磁盤 I/O。
異步編程意味著引擎在事件循環(huán)中運行。當(dāng)需要阻塞操作時,請求被啟動,代碼繼續(xù)運行而不阻塞結(jié)果。當(dāng)響應(yīng)就緒時,將觸發(fā)一個中斷,這會導(dǎo)致運行一個事件處理程序,控制流將在此處繼續(xù)。這樣,單個程序線程可以處理許多并發(fā)操作。
用戶界面本質(zhì)上是異步的,大部分時間都在等待用戶輸入來中斷事件循環(huán)并觸發(fā)事件處理程序。
默認(rèn)情況下,Node 是異步的,這意味著服務(wù)器以大致相同的方式工作,循環(huán)等待網(wǎng)絡(luò)請求,并在處理第一個請求時接受更多傳入請求。
這在 JavaScript 中很重要,因為它非常適合用戶界面代碼,并且對服務(wù)器性能非常有益。
標(biāo)簽: