一次失敗的技術面,讓我贏得了 5 個 offer

這簡直就像做夢一樣。我花了 6 個月的時間投入學習編程,並和女友一起搬到澳大利亞,又最終為了一份工作回到挪威。

這事看起來一帆風順,一切盡在掌握。我先給大家介紹一下我的創業背景。在過去的6個月裡,我一直在孜孜不倦地搞我的簡歷和個人項目,其中最亮眼的作品是我創建的 CryptoDasher,這是一種實時追蹤加密貨幣和投資組合價值的工具。我還向中國區塊鏈公司 Loopring 的網頁設計大賽提交了一份參賽作品。

我覺得自己已經準備好了。我向一家位於挪威的大型諮詢公司申請前端開發的職位,在提交了面試題和第一輪面試後,我被邀請參加技術面試。

大件事。我緊張。技術面試咋準備?

——我這麼問我自己。我瘋狂地搜索著互聯網,在YouTube上看模擬訪談。以下是我使用的一些資源:

  • Cracking the front-end interviewDavid Shariff 2017前端網頁開發面試準備每個JavaScript 開發工程師都該知道的十個面試問題Toptal’s list of JavaScript interview questions (JavaScript面試問題清單)Mozilla Developer Network (MDN) (Mozilla 開發者社區)Pramp - a tool for mock interviewing with others (Pramp – 模擬面試練習工具)Github Frontend developer questions collection (Github 前端工程師問題大全)YouTube JS mock interview #1 (YouTube JS 模擬面試 #1)YouTube JS mock interview #2 (YouTube JS 模擬面試 #2)

我埋頭於這些材料好久好久,試圖做好充分的準備。如果沒能在面試準備中竭盡全力我會生自己的氣——你肯定能理解這種感覺。

1號技術面

然後就是面試的日子。

而那天凌晨4點我就醒了。仍然害怕,但也很激動。我在公司的大廳裡見到面試官,隨後去他們辦公室。我們相談甚歡而且很快就找到了彼此的共同點。我很擅長軟技能,所以希望能儘早展示自己的實力。很快我們又會見另一個面試官,然後去了會議室

起初面試很順利,我們每個人介紹自己,他們問我一些背景問題,而我談了談開始編程時自己覺得最困難的部分,我想要學習哪些類型的技術,以及哪種技術我想分享給別人(並由衷地喜歡這麼做)

這個時候我覺得面試進行得非常順利——我想更瞭解這家公司而且我覺得和麵試官心有靈犀。然後技術面試的部分開始了。

一次失敗的技術面,讓我贏得了 5 個 offer

首先,他們要求我解釋代碼。這個任務要求給一個數據集創建分頁並顯示在一個列表中。我用React寫的,然後我開始檢查代碼,在整個遍歷過程中,面試官會問我問題。我試著把它們回憶出來然後寫在下面。

其中一道是:你是否知道什麼是單元測試?哪部分的代碼可以被單元測試?

老實說我覺得這個問題我答錯了。單元測試是一段代碼,用於驗證源代碼的模塊或某段特定部分是否執行其預期目的,並且未產生不想要的副作用。我不記得我說了什麼,但我可能把它和集成測試混在一起了。在面試之前,我確實對單元測試和TDD有一定的瞭解,但在當時的情境下我有點糊里糊塗。

你將如何改進這個項目?

我有點迷惑,幾周前我就被要求列出一份可改進的項目清單。如果面試官已經知道這些地方了,我也沒啥別的可改進的了……結果我拼命想也沒想出到底有什麼其他可以改善的地方。

不過很快我就意識到他們說的就是我郵件裡列出的那些點,於是我開始列舉這些要點——錯誤處理、移動端優化、在Ajax調用加載時的用戶反饋,以及在大型數據集中的頁面管理。

你知道BEM是什麼嗎?你在代碼中使用的是BEM嗎?

我回答說,我知道BEM是一個用於CSS項目的命名規範,意思是塊、元素、修飾符。我還回答說,我在CSS的類命名中受到了BEM的啟發,但它並不完全是BEM,因為它沒有完全遵循所有的BEM規則。

你如何使這個網站對移動設備友好?

CSS媒體請求(media query),這是最主要的方面。他們想知道我瞭解怎樣使用媒體請求來使網站響應。

到目前為止一切都好。我覺得回答很有競爭力,然後他們要求我擴展功能性,要我執行一種排序機制,接受分頁的數據集,並根據名稱和數字重新排列。我之有幾分鐘時間思考這個問題。

我問了些問題來明確對方的意思,比如我是否應使用內置的JavaScript排序函數,還是構建自己的(稍後會看到,這是一個很大的錯誤)。被分頁的數據作為對象數組存在,其中每個對象都有一個包含20個對象的數據數組,這些對象代表之前創建的那個列表中的每一個項目。我得出了下面的算法:

將每個分頁對象數據數組組合為一個新數組。

排序新數組。

將排序後的數組進行分頁,並將組件的狀態設置為新近排序的數組。

這個算法挺好,我很快就知道該怎麼做,現在唯一的問題是執行。接下去就是我犯的錯誤了。

首先,我花了太長時間去想明白怎樣合併這些數組。我承認部分是因為當時的壓力情境讓我有點不靈活,因為一個簡單的reduce語句就可以做的事情我卻想了各種奇怪的方法。話說回來我當時也實在沒有現在心情這麼輕鬆。

一次失敗的技術面,讓我贏得了 5 個 offer

現在我有了一個包含所有數據的數組,我需要寫邏輯來給它排序。由於我在編程方面的經驗很大程度上是建立在自己的項目基礎上的,所以我已經有很長一段時間沒有使用JavaScript排序函數了,必須先去查一查。我花了一些時間來檢查MDN和stack overflow上的例子,以便在執行之前先搞懂它。

排序部分地管用了。我在這裡卡了一會。數組中的大多數名稱都是正確排序的,但是在頂部有一些名稱是不按順序排列的。這個時候我試圖保持冷靜,但腦內已經崩潰了。我絞盡腦汁去想是怎麼回事,結果在這裡卡住的時間不忍直視。

在面試官討論和督促之後,我終於想起來字符串是按照它們的ASCII值排序的。大寫字母的值從65到90,小寫字母的值從97-122。頂部沒有正確排序是因為它有個大寫首字母,因為它們的ASCII值比小寫字母要低,所以會首先排它們。我這輩子都不會再犯這個錯了。

搞明白怎麼回事以後,我立即用.toLowerCase()解決掉了這個問題。

現在還有最後一件事。將已排序的數據傳到分頁函數中。在這裡,我遇到一個問題:分頁函數期望一個Ajax響應,並將每個項目傳給一個formatData函數,該函數將相關的部分分開,並返回一個新對象。然而,當我試圖傳遞被排序到這個函數的新數組時,它就沒有初始的屬性名了,然後函數就會拋出一個錯誤。

我花了點時間才發現,必須將formatData從分頁函數中移出,然後在數據傳給分頁函數之前就在響應數據上執行它。搞定這個以及其他一些小修改之後,代碼終於可以用了。花了不少時間,不過最終還是解決了。在這個時候,技術面試的代碼部分已經結束了。

我感到筋疲力盡。

結束之前我們又談了一會兒,他們告訴我更多公司的信息,我也在走之前又問了幾個問題。不過,面試並未就此結束。我仔細思考整個面試過程,反思自己做錯了哪些地方,然後才休息。

第二天,我花了三個小時改進解決方案,然後寫了封郵件:

我想感謝你們昨天能花時間和我聊聊。關於解決方案的問題,我想了很多,並決定今天再做一些改進。隨郵件附上我們昨天做的代碼的增強版本,以下是我所做的:

  • 我擴展了排序功能,按第二次可以反向排序結果。將排序功能擴展到所有的標題。為排序的數據頭部添加了一些圖標。重構了分頁函數,我學了單元測試的基礎知識,並使用Jest測試其功能。為分頁添加了查詢字符串支持,以便重載和鏈接可以在訪問不同頁面時顯示正確的數據。我添加了媒體查詢樣式,以使組件對移動設備友好...."

不久之後我收到了這封郵件:

"Hi!我們感謝您的面試,但我們暫時不能向您提供這個職位,因為你在技術方面沒有達到我們的期望。我們喜歡你的背景,相信你能很好地融入團隊,所以我們會就您的技術面試提供詳細的反饋,希望您能在獲得更多編程經驗後再次申請我司。"

我做錯了哪些部分?幸運的是,我得到了一份詳細的反饋。

反饋1:“花太多時間找出如何合併數組。先在線搜索而非查詢JavaScript技術文檔(例如:“js array doc”會給出w3schools或mdn,其中列出了函數),並錯誤地使用了這些示例(array.concat返回一個新數組)。沒人能記得api中的所有內容,所以善於使用JS或庫的文檔是很重要的。”

教訓:面試官想要看到你首先能使用MDN(或其他相關的文檔)。他們想看到你能夠找到並閱讀文檔,並基於其中的信息來執行。

反饋2:“在排序的任務中,面試者先提出了一種奇怪的手動算法。幸好他選擇用JavaScript內置的sort函數,但不確定這是如何工作的,因而必須反覆回去檢查文檔。”

教訓:在溝通的時候要儘可能講得清楚明白。在這個事情上,我問面試官是否應使用JS內置的分類函數,以求明確這個任務的邊界/限制,以及表明我在瞭解操作條款之前不會直接跳到寫代碼的步驟。不幸的是,我想我提出使用自己的分類算法的意圖被誤解了,實際上我不該這麼做,除非他們對這一任務有這種特定的要求。

結果適得其反。確定你們溝通無誤,因為你的意思對你自己是很清楚的,但面試官完全可能理解成相反的方向。

反饋3:代碼工作的時候,文本被設為“大小寫敏感”,這是很常見的情況。不幸的是面試者很久都沒搞懂怎麼回事,不過一旦理解狀況他很快就糾正了。

教訓:速度第一。寫程序出Bug很正常,但你得儘快解決。找到問題核心,沒想明白就馬上去文檔裡找。

反饋4:在代碼重構時花了些時間去理解為什麼formatData應從分頁函數中移出。

教訓:同上,速度第一。

反饋5:“許多循環語句是可以用array.map或array.reduce的。需學習更多函數式編程知識。

教訓:學習array.map, array.filter 和 array.reduce。之後我一直在鑽研函數式編程這個挺打擊人的東西。但是你不需要現在就學習一切,只要確保自己打好基礎就好了。

反饋6:我希望面試者能有更多單元測試的知識。

教訓:沒啥好說的,太明顯了——不過重要說三遍:測試很重要,測試很重要,測試很重要,去學,去搞懂,去用。

事後聰明誰都會,不過被拒絕以後你總歸會花點時間想想自己在哪些地方能做得有所不同。我花了太多時間在JavaScript知識上面,但實際上我應該花點時間去看看我自己寫的代碼。即使是我自己寫的,幾周以後的面試上你也需要重新鞏固一下記憶了。我希望我自己在這上面花點時間,而不是去操心那些模糊的JS問題。

我在面試前做了很多理論準備。但後來,我希望自己能花更多時間去做,或至少是摻了一點實際的任務,像在Hackerrank或CodeWars裡解算法題,或者構建些常見的前端組件,比如排序列表、下拉菜單、分頁等等。

面試結束後

第一次面試後感覺如何?老實說,這是一次很棒的經歷。我非常感謝面試官給了我如此詳細的反饋,還讓我可以在下一次面試之前糾正自己的錯誤。雖然沒有得到這份工作,但我離獲得第一個前端開發者職位邁進了一步。

我還學到了面試是件說不準的事。假如我曾在自己的項目裡做過排序機制,或者我的任務和以前做過的東西更相近,事情就可能不太一樣。

我最大的優勢是在過去的一年裡花大力氣學習JavaScript,現在也有能力快速學習和適應。不幸的是我這次沒機會展示這些優勢:

  • 我沒能向他們展示我對JavaScript原型鏈的瞭解,以及它如何讓對象之間的方法共享更輕鬆更省內存。
  • 沒能討論閉包,以及JavaScript內部函數如何在外部作用域內關閉變量,並在外部函數返回後訪問它們——這能防止垃圾收集。
  • 沒能分享我對JavaScript作用域的瞭解,以及JavaScript是如何在本地作用域進行逐級檢查直至全局來尋找變量的。
  • 分享我對變換的知識,以及===怎樣能不用類型變換檢查相等,怎樣用==檢查相等和類型轉換。
  • 沒能說到hoisting ,以及函數聲明和變量(除了let和const)是怎樣提到JavaScript頂部,以允許之前的代碼使用的。
  • 沒能談到“this”關鍵詞,以及這些變量

現在這種情況下我很容易認為:“我還不夠好,我該再花三四個月多學點東西,然後再申請一次。”——但我沒這麼做。

我決定再兩週裡申請儘可能多的工作,於是去申請了挪威那些最大的IT公司。要說我從第一輪面試學到了什麼,那就是準備很關鍵。最好是把技術面試看成一場考試,你要做盡可能完善的準備來確保通過。面試就跟考試一樣是有缺陷的:它不能包含面試者所有的知識譜系,那你該怎麼做呢?

——儘可能拓展你的知識面。像《黑客帝國》裡的尼奧一樣刀槍不入。

2號技術面試

從失敗中獲取教訓,我這次可做好功課了。

這一次,面試更多地集中在前端的概念討論上。這是一次綜合性面試,我感到面試官很想了解我的知識面,知道我的優劣勢所在。

以下是我們涉及到的主題:

JS, CSS and HTML broad strokes

Document structure文檔結構

Project structure

Git

Performance性能

Security安全

Accessability可訪問性

SEO

Responsive web design響應式網頁設計

這些代碼挑戰都是基於普通JavaScript的。我被考到在一個div里加上一個簡單類。現在如果你主要用的都是JS框架,你可能不太熟悉classList API。幸好我大多數時候都在用普通JS做freeCodeCamp項目,以下是做出來什麼樣:

一次失敗的技術面,讓我贏得了 5 個 offer

你也可以使用classList.toggle(‘new-class’) 把這些寫進一行裡。還有一個拓展任務是在下拉菜單外單擊就能關掉菜單:

一次失敗的技術面,讓我贏得了 5 個 offer

這一代碼挑戰的教訓:

越短越好,但要能讀。

性能相關。最好是把查詢選擇器放在事件監聽回調函數外面(僅回調一次,而非每次監聽警報都回調)

性能相關。getElementById 方法和 getElementByClassName方法 比querySelector 性能表現更好。

第二天經理給我打電話說我通過了面試,打算給我offer。

我可以就此停手,“我通過了一次技術面試,棒棒的。”不過恰恰相反,我給每個公司都打了電話,告訴他們我有offer了,能否加快流程,因為我現在考慮的時間比較有限了。

面試,尤其是技術面試,是嚴酷的精神折磨,你全程都在公開展覽自己,想要滿足甚至超出別人的預期,這很困難。我為什麼要這樣對自己?

出於以下四個理由:

1 我想證明得到這個offer不是我狗屎運;

2 我希望能夠尊重每一個面試官,給他們一次公平的機會;

3 我希望儘量找到自己最適合的崗位和最適合自己作為開發者的團隊;

4 因為你們:這個社區讓我受益良多,我想從技術面試中獲得儘可能多的信息放進資源中,你們也可以從我的失誤中學習和準備。我從freeCodeCamp中得到的幫助和支持使我謙卑,並希望有所回報。

3號技術面

在和其他公司接觸並解釋我已經有了一流公司的offer之後,不少公司加快了流程,我一週裡參加了幾次面試,並且有更多技術面試要參加。

以下是我第三次技術面試的問題集錦:

  • 你怎麼用上React的?為何要用它?它有什麼優勢?Redux怎樣工作?API由什麼組成?什麼是不可變性?不可變性有什麼好處?怎樣重新設計我們的網站?你對處理應用的更深層次怎麼看?請舉一個後端的例子。你會自己測試嗎?什麼是單元測試?好的用戶體驗是什麼?你如何測試用戶體驗?

代碼題是基於CSS的,他們給了我一張紙,上面有一些規則如下:

一次失敗的技術面,讓我贏得了 5 個 offer

我的任務是解釋這些內容。我立即識別出了HTML元素並告訴面試官,元素上的id和類可以用在CSS中,以選擇HTML元素。從這裡開始,我解釋了CSS是級聯的,這意味著一般來說最後一條規則會起效。然而在這個情境裡,選擇器有不同的權重。權重的順序如下:id>類>元素。

這意味著在上面那個例子裡,顏色黑將被應用到HTML元素。

4號技術面

這是我最後一次技術面試,雖然還是很驚心動魄不過我已經差不多習慣了。以下是我們談的大致內容:

  • 寫一個基本的網站。界定組建。如何使之響應?如何讓文本水平居中和垂直居中?什麼是CSS盒模型?內容盒和邊框盒的區別是什麼?雙等號和三重等號的區別是什麼?React有什麼好處?Array.forEach在一個for循環中有哪些益處?能否舉一些例子說明什麼時候你要用到一個for循環?

代碼題是構建一個適合多個難度的自動換行函數。想象你只能在一個屏幕裡放20個字,多於這個數,你就必須另起一行。

我一開始的解決方案是拆分字符串,用計數器和模數運算符來決定有沒有數到20,然後插入一個新行字符到數組裡並連接字符串。

然後問題增加了難度,只允許一行中顯示完整的詞,意味著如果一個詞讓計數超過20,那你得讓新行字符插在那個詞之前。

我沒能在面試中解決這個問題,不過方向是對的。我不太確定的時候用MDN,並且取得了不小的進展。

這就足夠了。我不能全部寫在這裡,如果你感興趣的話,以下是已解決版本:

一次失敗的技術面,讓我贏得了 5 個 offer

恭喜你讀到了這裡!文章很長,我儘可能給出詳細的信息,希望我的經驗能夠幫到大家。

最後的結果我一開始根本沒想到過:到最後我有5個offer隨便選。還有一家大公司甚至因為他們的競爭對手給了我offer就也給我發了一個。最後我選的是第一家通過技術面試的公司,因為我相信他們是最適合我的。

技術面試可能是個很難受的精神折磨,你會被難倒,被拽出自己的舒適區,不過這是好事,能幫你成長、使你變得更好。

如果你有所準備,就能迎頭而上。從我的經驗來看,不要回避技術面試。不要因為自己失敗過就拖延,不要認為它是你作為開發者道路上的終結手段。它不過是公司衡量你工作能力的最溫和的工具罷了。

去申請工作吧,做好準備去參加技術面試,從錯誤中學習並且重複以上步驟。我看好你噢。


分享到:


相關文章: