復盤一次失敗的技術面試后,我成功拿到了 5 個offer
英文原文:How I applied lessons learned from a failed technical interview to get 5 job offers
編者按:對于開發者來說,找工作最難的莫過于技術面試。Fredrik Strand Oseberg 在 freeCodeCamp 上發表了一篇文章,介紹了自己學習編程 6 個月后找工作獲得 5 個 offer 的經歷。
我先給大家介紹一下我的工作背景。
在過去的 6 個月里,我一直在孜孜不倦地研究我的投資組合和個人項目。最值得注意的是,我創建了 CryptoDasher,這是一種能夠實時跟蹤加密貨幣和投資組合價值的工具。我還參加了一家名為 Loopring 的中國區塊鏈公司舉辦的設計競賽。
我覺得已經準備好了。我向挪威一家大型咨詢公司投了簡歷,應聘其前端開發人員的工作,并引起了他們的注意——至少我是這么認為的。
通過筆試和***輪面試后,我被邀請參加技術面試。這是最重要的一個環節。
我很緊張。
“你應該怎們準備這個技術面試呢?”我問自己。我四處詢問,瘋狂地在互聯網上搜索相關資料。并在 YouTube 上看了一些模擬面試。以下是我使用的一些資源:
-
拆解前端面試(freeCodeCamp 上的文章)
-
David Shariff 的2017 年為Web 前端開發面試做準備
-
每個 JavaScript 開發者都應該知道的 10 個面試問題
-
Toptal 的 JavaScript 面試問題列表
-
Mozilla 開發者網絡(MDN)
-
Pramp - 與其他人進行模擬面試的工具
-
Github 前端開發者問題合集
-
YouTube JS 模擬面試1
-
YouTube JS 模擬面試2
我在這些材料上花了好幾個小時的時間,盡力為即將到來的面試做好準備。如果我在面試前沒有盡***的努力,我會感到很不舒服,我相信你明白那種感受。
面試的那天到了。我早上 4 點就醒了。
很害怕,很好奇,也很興奮。
我在公司的大廳里遇到了面試官,和他一起去了他們的辦公室。
我們之間的交談很愉快,并相互交換了聯系方式。我比較擅長軟技能,所以我希望能早點展示這種能力。很快,我又見到了另一位面試官,然后去了會議室。
面試剛開始很順利。我們每個人都進行了自我介紹,他們開始問我一些關于我的背景的問題。
他們問我,開始學習編程時,最困難的部分是什么,我想學習什么樣的技術,我想要教別人什么技術,以及我覺得令人興奮的東西。
在這一點上,我覺得面試很順利。我很想知道更多關于這家公司的信息,我覺得已經與面試官在某些方面有了共鳴。
然后技術部分開始了。
首先,我被要求解釋我在筆試中的代碼。是一個為數據集創建分頁,并將其顯示在列表中的任務。我用 React 編寫了它,然后我開始檢查代碼。當我們瀏覽這些代碼時,我的面試官會問我一些問題。我將試著把他們提出的問題列出來,以及給出我認為的面試官想要的答案。
你知道單元測試是什么嗎?代碼的哪一部分可以進行單元測試?
說實話,我想我回答錯了。單元測試是一段代碼,用于驗證一個單元或源代碼的特定部分是否執行了它的預期目的,而不會產生不必要的副作用。我不記得我說了什么,但我可能把它和集成測試混在一起了。在面試之前,我確實對單元測試和 TDD 有一定的了解,但在這種程度上,可能已經超出了我的理解范疇。
在進行了一些討論之后,我得出結論:我可以對分頁函數進行測試,因為它對程序中的大部分邏輯負責。
你將如何改進這個項目?
我發現這個問題有點令人困惑。當我完成筆試(幾周前)時,我就被要求列出一份關于該項目的改進清單。假設面試官已經知道了這些,我就很難找到什么改進空間了。
我很快就明白了,面試官對我在電子郵件中提到的事情很感興趣,于是我開始提到這些點——錯誤處理、移動優化、Ajax 調用加載時的用戶反饋、以及大型數據集的頁面管理。
你知道 BEM 是什么嗎?你在代碼中使用的是 BEM 嗎?
我回答說我知道 BEM 是什么。這是一個用于 CSS 項目的命名約定,代表 Block、Element、Modifier。我還回答說,在我的 CSS 類命名中,我受到了 BEM 的啟發,但它并不完全是 BEM,因為它沒有遵循所有的 BEM 規則。
你如何使這個網站更具移動友好度?
CSS 媒體查詢。這是最主要的一個。他們想知道,我是否知道如何利用媒體查詢來讓網站做出響應。
到目前為止。面試進展很好。我覺得我很完整地回答了這些問題,盡管我需要在了解面試官的具體想問什么之前,先討論一下這些問題。
編程挑戰
然后他們要求我擴展功能。我被要求實現一種排序機制,該機制將采用分頁數據集,并根據名稱和編號對它們進行重新排列。我有幾分鐘時間來思考這個問題。
我問了一些問題,比如我是否應該使用內置的 JavaScript 排序函數,或者構建自己的函數(稍后我們會看到,這是一個很大的錯誤)。分頁數據以對象數組的形式存在,其中每個對象都有一個包含 20 個對象的數據數組,這些對象代表列表中的每一個項目。我提出了以下的算法:
1、將每個分頁對象數據數組合并到一個新的數組中;
2、對新數組進行排序;
3、對排序后的數組進行分頁,并將組件的狀態設置為新近排序的數組。
這是一個很好的算法。我很快就知道該怎么做了?,F在唯一的問題是實施它。這就是我犯錯的地方。
首先,我花了很長時間來找出如何組合這些數組。我承認,這種情況給我帶來了一些壓力。因為我本可以用一個簡單的 reduce 來解決它的時候,我做了各種奇怪的事情。公平地說,我當時并不像現在這樣熟悉 reduce。
//我應該做的 const pageData = pages.reduce((startingValue,page)=> startingValue.concat(page.data),[]) //我最終做的 const pages = this.state.pages; const pageData = [];pages.forEach(page => pageData = pageData.concat(page.data));
現在我有了一個包含所有數據的數組,我需要編寫邏輯來對其進行排序。由于我在編程方面的經驗,在很大程度上是基于我自己構建的項目,所以我花了很長時間來處理 JavaScript 排序函數。我必須要查資料,所以,我花了一些時間來檢查 MDN 和 stack overflow 上的例子,以便我在實現它之前真正理解它。
我只是部分地完成了分揀工作,我被困在這里好一段時間。數組中的大多數名稱都是正確排序的,但是在頂部有一些名稱是無序的。在這個時候上,我試圖保持冷靜,但在我的腦海里,已經崩潰了。我想要知道為什么它沒有正確排序。我被困在這里的時間比我想承認的要長。
經過面試官的討論和督促。我最終想起來了字符串是按照它們的 ASCII 值排序的。大寫字母的值是 65-90,小寫字母的值是 97-122。沒有正確排序的結果有一個大寫的首字母,它具有先排序的效果,因為它們的 ASCII 值比小寫字母要低。這是一個我永遠不會再犯的錯誤。
當找到問題后,我立即用用 .toLowerCase()解決了這個問題。
現在只剩下一件事了。
將已排序的數據傳遞到分頁函數中。
在這里,我遇到了一個麻煩。
分頁函數需要一個 Ajax 響應,并將每一項傳遞給一個 formatData 函數,該函數提取相關片段并返回一個新對象。然而,當我試圖傳遞被排序的數據到這個函數的新數組時,它將不再具有原來的屬性名,并且函數會給出一個錯誤。
我花了一些時間研究這個問題,然后我才發現我必須將 formatData 從分頁函數中移出,并在數據傳遞給分頁函數之前在響應數據上執行它。
完成了這些工作,并進行了一些更小的修改,代碼終于可以工作了。雖然花了一些時間,但最終我解決了。
此時,技術面試的編程部分結束了。
我感到精疲力竭。
我們***又聊了一會兒,然后結束了面試。在結束之前,我問題一些問題,他們告訴了我更多關于他們公司的事情。
然而,面試并沒有就此止步。
我仔細復盤了這次面試,琢磨我做錯了什么。
第二天,我花了三個小時來改進解決方案,然后我發了這封郵件:
嗨,面試X和面試官y。
我想感謝你們昨天同意和我交流。我已經思考了很多關于這個問題的解決方案,我決定今天就改進它。我提供了我們昨天工作的增強版本的代碼。這是我所做的:
我擴展了排序功能,以便能夠在第二次按下時逆轉結果。
我將分類功能擴展到所有的 titles。
我添加了一些圖標來對 titles 進行排序。
我重構了分頁函數,學習了單元測試的基礎知識,并使用 Jest 來測試它的功能。
我增加了對分頁的查詢字符串支持,這樣重載和鏈接就會在訪問不同的頁面時顯示正確的數據。
我添加了媒體查詢樣式,使組件更具移動友好度。
在 API 調用發生時,我添加了一個加載器。
我添加了錯誤處理,讓用戶有機會重新啟動 API 調用。
我在移動設備上改變了排序功能,并使用了一個選擇框。
......
這可能有點矯枉過正,但我很受啟發,我想要改進解決方案。
***的問候,
Fredrik Strand Oseberg
這還不夠。但至少我盡了***的努力。過了一段時間,我收到了這封郵件:
嗨!
我們想感謝你的面試,但我們必須得出這樣的結論:我們不能給你這個職位的 offer,因為你在技術方面沒有達到我們的期望。
我們喜歡你的背景,相信你能很好地融入我們的社區,所以我們在你的技術面試中給了你一份詳細的反饋,希望你能在獲得更多編程經驗后再申請我們的職位。
我在哪里出錯了?
幸運的是,我得到了一份詳細的反饋報告。讓我們來看看吧,我將和你們討論其中的內容。
反饋1:“花費太多的時間來了解如何組合數組。首先在互聯網上搜索,而不是檢查 JavaScript 文檔(例如:“js array doc”將提供 w3schools 或 MDN,其中列出了函數),并錯誤地使用了這些示例(array.concat 返回一個新數組)。沒有人會記住 API 中的所有內容,所以能夠很好地使用 JS 或庫的文檔是很重要的。”
要點:面試官希望你首先接觸到 MDN(或其他相關文檔)。他們希望看到你能夠找到并閱讀文檔,并根據發現的信息來實施它。
反饋2:“在排序分配中,候選人首先提出了一個奇怪的手動算法。幸運的是,他選擇在 JavaScript 中使用內置的排序功能,但是不確定它是如何工作的,并且必須反復檢查文檔。”
要點:在交流中要絕對清楚。在這種情況下,我詢問了面試官關于我是否應該使用內置的 JavaScript 排序功能,以搞清楚手頭上任務的界限和限制。不幸的是,我認為這被誤解為我建議自己使用的排序算法。
這最終產生了和我想要傳達的相反的效果。確保你清楚地表達出你的問題想要澄清的東西。因為它們可能對你來說很有意義,但你的面試官可能會對此有所察覺。
反饋3:“當代碼運行時,文本被排序為“區分大小寫”。不幸的是,候選人花了很長時間才明白這個問題,但一旦被發現,就立即改了過來。”
要點:速度是最重要的。在編寫程序時,總是會出現 bug,但是要盡可能快地解決它們。找到問題的根源,如果你不知道,就迅速地去查文檔。
反饋4:“花了一些時間來理解為什么要在重構的時候將 formatData 移出分頁。”
再說一遍,速度是最重要的。
反饋5:“許多 foreach 循環,其中可以用數組 .map 或 array.reduce 來解決。了解更多函數式編程將是有益的。”
要點:學習數組 .map、array.filter 和 array.reduce,并熟練地掌握它們。我一直在鉆研函數式編程,這是一項艱巨的任務。但是你現在不需要完全精通這些知識,只要確保你能掌握了基礎知識就行。
反饋6:“我希望候選人對單元測試有更多的了解。”
要點:這似乎是顯而易見的,但重要的問題要多說幾遍:測試很重要。測試很重要。測試很重要。學習它。使用它。
這份文件的其余部分都是贊揚。我不會說太多細節,因為它沒那么重要。要點是:
-
他很好地使用了編輯器
-
他在 Chrome 中使用調試器(了解高級調試工具很重要)
-
在繼續工作之前,他會檢查這些東西是否正常工作(使用 console.log)
-
他試圖將代碼分成更小的邏輯部分
-
他使用變量名而不是注釋,這使得代碼可讀性更好
-
他很了解 React
-
之前的項目令人印象深刻
-
擁有編程之外(設計/視覺)其他積極的品質
在準備過程中,我還能做些什么?
當你被拒絕的時候,你將不可避免地花費一些時間來思考你可以做些什么不同的事情。
更徹底地檢查筆試代碼
我花了太多時間研究我的 JavaScript 知識。我應該更了解我自己的代碼。盡管我寫了這篇文章,但在寫作和面試之間的幾周時間里,你需要回顧一下。我希望我在這上面花的時間比在模糊的 JavaScript 問題上更多。
做更多實操性的 JavaScript 任務
在面試前我做了很多理論工作。我現在希望我能夠花更多的時間做更多的實際工作,或者至少是混合了一些實際的工作,或者構建一些常見的前端組件,比如排序列表、下拉菜單、分頁等等。
面試結束
在***次技術面試結束后我感覺如何?老實說,這是一次很棒的經歷。我非常感謝面試官,他們給了我如此詳細的反饋,讓我能夠在下一次面試前糾正我的錯誤。盡管我沒有得到這份工作,但我離成為***個前端開發者的工作又近了一步。
我也了解到面試是一件反復無常的事情。也許如果我在自己的項目中構建了一個排序機制,或者如果我得到的是一個與我之前完成的任務更接近的任務,面試結果將會有所不同。
我***的優勢是在過去的一年中我花了很多時間學習 JavaScript,現在我能夠很快地學習和采納新的想法。不幸的是,我這次沒有能力證明這一點。
通往成功之路
現在,我很容易對自己說:“我還不夠好。我需要花3-4 個月的時間來學習,然后再試一次。”
但我沒有。
我決定在兩周內盡可能多的申請工作。我向挪威***的 IT 公司投遞了簡歷。
兩周后,我完成了幾家公司的初步面試,然后我又接受了技術面試。
第二輪準備
我在***次技術面試中學到的一件事是,準備工作很關鍵。它可以幫助你把技術面試變成是一場考試,并采取必要的步驟來確保你通過考試。
但將考試比作面試是錯誤的,因為它沒有涵蓋候選人的全部知識范圍。那你能做什么呢?
擴大你的知識范圍。
我使用了先進的記憶策略,在 8 個小時內記住了超過 100 個面試問題的答案。這些問題可以在這個數據庫中找到。
此外,我還在在 Code Wars 和 Hackerrank 的實例上花了很多時間。并花了很多時間來構建一些事物。
第二次技術面試
我在上次失敗的面試中吸取了很多教訓,我做了很多的準備。
這次面試的重點是討論前端概念。這是一次全面的面試,我覺得面試官想搞清楚我的知識范圍,并弄清楚我的強項和弱項。
這次面試持續了大約兩個小時。以下是我們所討論的所有主題的列表:
-
JS,CSS 和 HTML 概述
-
文檔結構
-
項目結構
-
Git
-
性能
-
安全
-
可訪問性
-
搜索引擎優化
-
響應式網頁設計
編程挑戰是基于 vanilla Javascript 的。我被要求用普通的 Javascript 將一個簡單的類添加到一個 div 中?,F在,如果你已經花時間用 JS 來使用主要的框架,你可能不熟悉 classList API。幸運的是,我大部分時間都花在了所有的 freeCodeCamp 項目上。這就是它的樣子:
- const btn = document.querySelector ('.btn');
- const menu = document.querySelector ('.menu');function addClassNameToDiv () {
- if (!menu.classList.contains ('new-class')) {
- menu.classList.add ('new-class');
- } else {
- menu.classList.remove ('new-class');
- }}
- btn.addEventListener ('click', addClassNameToDiv)
或者,您可以使用 classList.toggle('new class')將其轉換為一行程序。如果你點擊下拉菜單,我還被要求將它擴展至關閉菜單:
- window.addEventListener ('click', () => menu.classList.remove ('new-class'));
從編程挑戰中獲得的信息是:
-
越短越好,只要它總是可讀的
-
在性能方面,***將查詢選擇器置于事件監聽器回調函數之外(只調用一次,而不是每次都觸發)
-
性能方面,getElementById 和 getElementByClassName 比 querySelector 更好
第二天,我接到了經理的電話。我通過了面試,他們想給我一個機會。我本可以在這里停下來,不用參加其他的面試了。我可以說:“我已經拿到了一個 offer,這已經足夠了。”
但我做了相反的事情。
我打電話給所有我正在面試的公司,并告訴他們我已經收到了一個 offer,并問他們是否可以加快進程,因為我現在有時間限制。
面試,尤其是技術面試,都是很艱難的心理考驗。如果你一直在展示,面試官將期待你的表現能夠超越預期。這很難。那么我為什么要這么做呢?
原因有四個。
-
1、我想向自己證明,這不是運氣。
-
2、我想要尊重每一個給我面試機會的人,給他們一個公平的機會。
-
3、我想確保自己找到了適合自己的公司,讓我成為一名開發人員。
-
4、為了你們,這個社區對我的幫助很大,我想從技術面試中獲得盡可能多的信息,這樣你們就可以從我的錯誤中吸取教訓,并做出相應的準備。
我對我從 freeCodeCamp 獲得的幫助和支持感到慚愧,我想要回報。
第三次技術面試
在與其他公司取得聯系,并表明我獲得了一家***公司的 offer 后,很多公司都迫不及待地想讓我通過面試。在一周內,我完成了幾次技術面試。
以下是第三次技術面試中的一些問題:
-
你是如何學習 React 的?你為什么要學習它?這有什么好處?
-
Redux 是如何工作的?這個 API 由什么組成的?什么是不變性?不變性的好處是什么?
-
你將如何重新設計我們的網頁?
-
你如何處理更深層次的應用程序?例如后端?
-
你自己做測試嗎?什么是單元測試?
-
對你來說,什么是好的用戶體驗?
-
如何測試用戶體驗?
這次面試中的編程挑戰是基于 CSS 的。
我收到了一張紙,上面有一些 CSS 規則,看起來是這樣的:
- <div ></div> // HTML Element
- // CSS Rules
- #menu {
- color: black;
- }.dropdown-menu {
- color: green;
- }div {
- color: blue;
- }
我的任務是解釋我所看到的。我立即識別了了 HTML Element 并告訴面試官,element 上的 id 和 class 可以在 CSS 中使用,以選擇 HTML Element。在這里,我解釋說 CSS 是級聯的,這意味著通常***一條規則將適用。然而,在這種情況下,選擇器有不同的權重。順序如下所示:id> class>element。
這意味著,在上面的示例中,黑色將被應用到 HTML Element 中。
第四次技術面試
這是我進行的***一次技術面試。雖然它仍然很傷腦筋,但現在我已經習慣了。下面是我們討論的內容:
-
建立一個基本的網站。確定其中的組件。
-
你如何讓它響應?
-
如何將文本垂直和水平居中?
-
什么是 CSS 框模型?內容框和邊框之間的區別是什么?
-
React 有什么好處?
-
array.forEach 在 for 循環中的好處是什么?有沒有可能需要使用 for 循環的情況?
編程挑戰是建立一個不同程度難度的 wordwrap 函數。想象一下,你只能在屏幕上放 20 個字符,如果你超過它,你就得從一個新行開始。
我對這個問題的原始解決方案涉及拆分字符串,使用計數器和模數運算符來確定計數是否為 20,然后在數組中插入一個換行符并加入字符串。
然后,任務難度增加了,只允許全部單詞排成一行。也就是說,如果一個單詞導致總數超過 20,那么需要在單詞前面插入一個換行符。
我在面試中并沒有完全解決這個問題,但我的思路是正確的。在我不確定的時候,我使用了 MDN,并且我取得了很好的進展。
這就足夠了。
我不能把它寫下來,如果你感興趣的話,這里有一個解決的版本:
- function wordWrap (str) {
- let totalCount = 0;
- const arr = str.split (' '), formattedStr = [];
- arr.forEach ((word, index) => {
- totalCount += word.length;
- if (totalCount >= 20) {
- formattedStr.push ('\n', word, ' '); totalCount = word.length;
- } else {
- formattedStr.push (word, ' ');
- }
- }); return formattedStr.join ('');
- }
結論
如果看到了這里,恭喜你。這是一個漫長的過程。我盡可能提供更多的信息,希望它能幫助像你這樣的人。
這樣做的結果是,我陷入了一個我從未想過的境地。***,我有 5 個 offer 可供選擇。一家大公司甚至給我提供了一個“blind”offer,不管競爭對手給我多少錢,它都能更高。我最終選擇了我***次通過技術面試的公司,因為我相信這對我來說是最合適的。
技術面試可能是一場艱苦的精神折磨。你會受到挑戰,你會被帶出你的舒適區,這是一件好事。它能幫助你成長。它會讓你變得更好。
如果你準備好了,你就能有所收獲。
所以從我的經驗來看,不要回避技術面試。不要因為你失敗了就放棄。不要認為這是你作為開發者的***衡量標準。它不是。它只是公司用來衡量你的生產力的最簡單的工具。
申請工作。準備好。參加技術面試。從錯誤中學習。不斷重復這一過程。
如果你這樣做,我保證你會成功。