對大部分公司來講,招聘技術人員這種事情,管理層就應該放手交給技術團隊,只有他們纔可以準確地判斷應聘者的技術實力。若是你恰巧是應聘者,你也是早晚都要去面試的。無論你是哪邊的,都讓大哥來教你幾招。前端
大兄弟們,要收藏,也要點贊關注吶。
優秀的團隊纔是決定公司業績的關鍵,一家公司要想於逆境之中仍能有所建樹,最重要的就是得先培養出一隻優秀的團隊。面試
就像 Marcus Lemonis 說的,有三點(3 個 P)最重要:算法
員工(People),流程(Process),產品(Product)。
在創業初期,你招來的工程師必須是可以獨當一面的大神隊友。他最好可以幫着招聘工程師,能指導其它工程師,還能幫初級和中級工程師解決各類問題。這樣優秀的隊友,不管什麼時候都多多益善。編程
要評估一個應聘者的真實水準,最佳方式就是結對編程(pair programming)。
和應聘者結對編程,一切都聽應聘者的。多觀察、多聆聽,看看應聘者是個怎樣的人。用微博的 API 抓取消息並顯示在時間線上,就是個很好的考察應聘者的面試項目。設計模式
不過結對編程再好使,也沒辦法讓你徹底瞭解一個應聘者。這個時候,面試也能幫上不少忙——可是千萬別浪費時間去問一些語法(syntax)或者語言上的細節(language quirks)——問些高端的問題吧,大兄弟。問問項目架構(architecture),編程範式(paradigms),這個層面上的判斷(the big desicions)可以在很大程度上影響一個項目的成敗。數組
語法和語言特性(features)這種小知識,Google 一搜一大把,誰都會。而工程師在工做中所積累的軟件工程方面的經驗,以及我的經常使用的編程範式及代碼風格(idioms),這些可都是很難 Google 到的寶貴財富。安全
JavaScript 很獨特,它在各類大型項目中都起着相當重要的做用。那是什麼讓 JavaScript 如此不同凡響?性能優化
下面幾個問題,也許能幫你一探究竟。網絡
JavaScript 是一門多範式(multi-paradigm)的編程語言,它既支持命令式(imperative)/面向過程(procedural)編程,也支持面向對象編程(OOP,Object-Oriented Programming),還支持函數式編程(functional programming)。JavaScript 所支持的面向對象編程包括原型繼承(prototypal inheritance)。閉包
函數式編程,是將數學函數組合起來,而且避免了狀態共享(shared state)及可變數據(mutable data),由此而產生的編程語言。發明於 1958 年的 Lisp 就是首批支持函數式編程的語言之一,而 λ 演算(lambda calculus)則能夠說是孕育了這門語言。即便在今天,Lisp 這個家族的編程語言應用範圍依然很廣。
函數式編程但是 JavaScript 語言中很是重要的一個概念(它但是 JavaScript 的兩大支柱之一)。ES5 規範中就增長了不少經常使用的函數式工具。
類繼承(Class Inheritance):實例(instances)由類繼承而來(類和實例的關係,能夠類比爲建築圖紙和實際建築 🏠 的關係),同時還會建立父類—子類這樣一種關係,也叫作類的分層分類(hierarchical class taxonomies)。一般是用 new
關鍵字調用類的構造函數(constructor functions)來建立實例的。不過在 ES6 中,要繼承一個類,不用 class
關鍵字也能夠。
原型繼承(Prototypal Inheritance):實例/對象直接從其它對象繼承而來,建立實例的話,每每用工廠函數(factory functions)或者 Object.create()
方法。實例能夠從多個不一樣的對象組合而來,這樣就能選擇性地繼承了。
在 JavaScript 中,原型繼承比類繼承更簡單,也更靈活。
面向對象編程的優勢:關於「對象」的一些基礎概念理解起來比較容易,方法調用的含義也好解釋。面向對象編程一般使用命令式的編碼風格,聲明式(declarative style)的用得比較少。這樣的代碼讀起來,像是一組直接的、計算機很容易就能遵循的指令。
面向對象編程的不足:面向對象編程每每須要共享狀態。對象及其行爲經常會添加到同一個實體上,這樣一來,若是一堆函數都要訪問這個實體,並且這些函數的執行順序不肯定的話,極可能就會出亂子了,好比競爭條件(race conditions)這種現象(函數 A 依賴於實體的某個屬性,可是在 A 訪問屬性以前,屬性已經被函數 B 修改了,那麼函數 A 在使用屬性的時候,極可能就得不到預期的結果)。
函數式編程的優勢:用函數式範式來編程,就不須要擔憂共享狀態或者反作用了。這樣就避免了幾個函數在調用同一批資源時可能產生的 bug 了。擁有了「無參風格」(point-free style,也叫隱式編程)之類的特性以後,函數式編程就大大簡化了,咱們也能夠用函數式編程的方式來把代碼組合成複用性更強的代碼了,面向對象編程可作不到這一點。
函數式編程更偏心聲明式、符號式(denotational style)的編碼風格,這樣的代碼,並非那種爲了實現某種目的而須要循序漸進地執行的一大堆指令,而是關注宏觀上要作什麼。至於具體應該怎麼作,就都隱藏在函數內部了。這樣一來,要是想重構代碼、優化性能,那就大有可爲了。(譯者注:以作一道菜爲例,就是由 買菜
-> 洗菜
-> 炒菜
這三步組成,每一步都是函數式編程的一個函數,無論作什麼菜,這個流程都是不會變的。而想要優化這個過程,天然就是要深刻每一步之中了。這樣無論內部如何重構、優化,總體的流程並不會變,這就是函數式編程的好處。)甚至能夠把一種算法換成另外一種更高效的算法,同時還基本不須要修改代碼(好比把及早求值策略(eager evaluation)替換爲惰性求值策略(lazy evaluation))。
利用純函數進行的計算,能夠很方便地擴展到多處理器環境下,或者應用到分佈式計算集羣上,同時還不用擔憂線程資源衝突、競爭條件之類的問題。
函數式編程的不足:代碼若是過分利用了函數式的編程特性(如無參風格、大量方法的組合),就會影響其可讀性,從而簡潔度有餘、易讀性不足。
大部分工程師仍是更熟悉面向對象編程、命令式編程,對於剛接觸函數式編程的人來講,即便只是這個領域的一些的簡單術語,均可能讓他懷疑人生。
函數式編程的學習曲線更陡峭,由於面向對象編程太普及了,學習資料太多了。相比而言,函數式編程在學術領域的應用更普遍一些,在工業界的應用稍遜一籌,天然也就不那麼「平易近人」了。在探討函數式編程時,人們每每用 λ 演算、代數、範疇學等學科的專業術語和專業符號來描述相關的概念,那麼其餘人想要入門函數式編程的話,就得先把這些領域的基礎知識搞明白,能不讓人頭大麼。
千萬別用類繼承!或者說盡可能別用。若是非要用,就只用它繼承一級(one level)就行了,多級的類繼承簡直就是反模式的。這個話題(不太明白是關於什麼的……)我也參與討論過好些年了,僅有的一些回答最終也淪爲 常見的誤解 之一。更多的時候,這個話題討論着討論着就沒動靜了。
若是一個特性有時候頗有用
但有時候又很危險
而且還有另外一種更好的特性能夠用
那 務必要用另外一種更好的特性
~ Douglas Crockford
React.Component
。原型繼承能夠分爲下面幾類:
Object.assign()
)上面這三種原型繼承都有各自的適用場景,不過它們都頗有用,由於都能實現組合繼承(composition),也就是創建了 A 擁有特性 B(has-a)、A 用到了特性 B(uses-a) 或者 A 能夠實現特性 B(can-do) 的這樣一種關係。相比而言,類繼承創建的是 A 就是 B 這樣一種關係。
Object.assign()
。這句話引用的是《設計花紋》(Design Patterns,設計模式)這本書的內容。意思是要想實現代碼重用,就應該把一堆小的功能單元組合成知足需求的各類對象,而不是經過類繼承弄出來一層一層的對象。
換句話說,就是儘可能編程實現 can-do、has-a 或者 uses-a 這種關係,而不是 is-a 這種關係。
雙向數據綁定(two-way data binding),意味着 UI 層所呈現的內容和 Model 層的數據動態地綁定在一塊兒了,其中一個發生了變化,就會馬上反映在另外一個上。好比用戶在前端頁面的表單控件中輸入了一個值,Model 層對應該控件的變量就會馬上更新爲用戶所輸入的值;反之亦然,若是 Modal 層的數據有變化,變化後的數據也會馬上反映至 UI 層。
單向數據流(one-way data flow), 意味着只有 Model 層纔是單一數據源(single source of truth)。UI 層的變化會觸發對應的消息機制,告知 Model 層用戶的目的(對應 React 的 store
)。只有 Model 層纔有更改應用狀態的權限,這樣一來,數據永遠都是單向流動的,也就更容易瞭解應用的狀態是如何變化的。
採用單向數據流的應用,其狀態的變化是很容易跟蹤的,採用雙向數據綁定的應用,就很難跟蹤並理解狀態的變化了。
採用單體架構(monolithic architecture)的應用,各組件的代碼是做爲一個總體存在的,組件之間互相合做,共享內存和資源。
而微服務架構(microservice architecture)則是由許許多多個互相獨立的小應用組成,每一個應用都有本身的內存空間,應用在擴容時也是獨立於其它應用進行的。
單體架構的優點:大部分應用都有至關數量的橫切關注點(cross-cutting concerns),好比日誌記錄,流量限制,還有審計跟蹤和 DOS 防禦等安全方面的需求,單體架構在這方面就頗有優點。
當全部功能都運行在一個應用裏的時候,就能夠很方便地將組件與橫切關注點相關聯。
單體架構也有性能上的優點,畢竟訪問共享內存仍是比進程間通訊(inter-process communication,IPC)要快的。
單體架構的劣勢:隨着單體架構應用功能的不斷開發,各項服務之間的耦合程度也會不斷增長,這樣一來就很難把各項服務分離開來了,要作獨立擴容或者代碼維護也就更不方便了。
微服務的優點:微服務架構通常都有更好的組織結構,由於每項服務都有本身特定的分工,並且也不會干涉其它組件所負責的部分。服務解耦以後,想要從新組合、配置來爲各個不一樣的應用提供服務的話,也更方便了(好比同時爲 Web 客戶端和公共 API 提供服務)。
若是用合理的架構來部署微服務的話,它在性能上也是頗有優點的,由於這樣一來,就能夠很輕鬆地分離熱門服務,對其進行擴容,同時還不會影響到應用中的其它部分。
微服務的劣勢:在實際構建一個新的微服務架構的時候,會遇到不少在設計階段沒有預料到的橫切關注點。若是是單體架構應用的話就很簡單,新建一箇中間件(shared magic helpers 不知道怎麼翻譯……)來解決這樣的問題就好了,沒什麼麻煩的。
可是在微服務架構中就不同了,要解決這個問題,要麼爲每一個橫切關注點都引入一個獨立的模塊,要麼就把全部橫切關注點的解決方案封裝到一個服務層中,讓全部流量都從這裏走一遍就好了。
爲了解決橫切關注點的問題,雖然單體架構也趨向於把全部的路由流量都從一個外部服務層走一遍,可是在這種架構中,能夠等到項目很是成熟以後再進行這種改造,這樣就能夠把還這筆技術債的時間儘可能日後拖一拖。
微服務通常都是部署在虛擬機或容器上的,隨着應用規模的不斷增長,虛擬機搶工做(VM wrangling work)的狀況也會迅速增長。任務的分配通常都是經過容器羣(container fleet)管理工具來自動實現的。
在同步編程中,代碼會按順序自頂向下依次執行(條件語句和函數調用除外),若是遇到網絡請求或者磁盤讀/寫(I/O)這類耗時的任務,就會堵塞在這樣的地方。
在異步編程中,JS 運行在事件循環(event loop)中。當須要執行一個阻塞操做(blocking operation)時,主線程發起一個(異步)請求,(工做線程就會去執行這個異步操做,)同時主線程繼續執行後面的代碼。(工做線程執行完畢以後,)就會發起響應,觸發中斷(interrupt),執行事件處理程序(event handler),執行完後主線程繼續日後走。這樣一來,一個程序線程就能夠處理大量的併發操做了。
用戶界面(user interface,UI)自然就是異步的,大部分時間它都在等待用戶輸入,從而中斷事件循環,觸發事件處理程序。
Node.js 默認是異步的,採用它構建的服務端和用戶界面的執行機制差很少,在事件循環中等待網絡請求,而後一個接一個地處理這些請求。
異步在 JavaScript 中很是重要,由於它既適合編寫 UI,在服務端也有上佳的性能表現。
多問問應聘者高層次的知識點,若是能講清楚這些概念,就說明即便應聘者沒怎麼接觸過 JavaScript,也可以在短短几個星期以內就把語言細節和語法之類的東西弄清楚。
不要由於應聘者在一些簡單的知識上表現不佳就把對方 pass 掉,好比經典的 CS-101 算法課,或者一些解謎類的題目。
面試官真正應該關注的,是應聘者是否知道如何把一堆功能組織在一塊兒,造成一個完整的應用。
推薦:你們能夠關注我,私信發送‘架構’便可獲取如下資料,裏面有源碼分析、性能優化、微服務架構、工程化、分佈式等知識點。走的就是高端路線 下圖是資料的一部分知識點 有用沒用一看就知道的: