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