1. 程式人生 > 實用技巧 >前端開發面試快速覆盤,不標準的面試經驗分享(三),逃離湖泊投身大海的魚

前端開發面試快速覆盤,不標準的面試經驗分享(三),逃離湖泊投身大海的魚

壹 ❀ 引

大致上來說,這是我認真找工作的第二週了,比較遺憾,到目前為止我仍為找到滿意的工作。倘若你問我找工作難不難,單站在找到工作的角度其實並不難,但如果在找到的基礎上增加一些條件就比較難了。就像兩個年輕人準備談戀愛,首先得互相看對眼才有初步機會,若一方要求較高那麼在一起的概率就會降低。在本週我從面試邀請中挑了三家看似不錯公司並參加了面試,大致都是有成熟上線專案的技術自研型公司。本篇文章中的問題可能與之前的面試覆盤存在一定差異,文章較長但我還是希望你能耐心讀完,並假想當時被問到的是自己又該如何作答,這對於日後大家挑戰高階開發崗位多多少少會有幫助(雖然我自己都還沒成功),那麼本文開始。

貳 ❀ 阿里某合作商

這是一家為阿里巴巴提供雲端計算服務的公司,技術算很硬了,要求也很高。我在週二晚上接的電話一面,後來才知道電話對面是他們公司的技術總監,談話內容主要圍繞JS基礎問題,大致如下(注意,下文中面試自我介紹我都省略了):

面:你在上家公司扮演了什麼角色?主要負責了哪些工作?

角色方面如實作答就好了,負責工作可圍繞在職期間自己工作出色點,或者其它能體現自己能力的方面作答,不要僅限於寫程式碼。比如我在專案重構方面出力較多,也負責了專案元件庫的開發,因為個人JS基礎知識儲備還行,所以也有帶見習開發的工作,所以我就圍繞這三個方面說了,大家可以構思總結下自己的工作出色點。

面:能說下深拷貝嗎?

深拷貝是針對引用資料出現的問題,引用我們將一個物件A賦予物件B時,物件B儲存的其實是A物件值存放地址的引用,所以如論修改A還是B,都會影響另一方。而深拷貝就是要達到複製物件後,修改任意一方都不會影響另一方。

面:那你能不能實現一個深拷貝?說下實現時需要考慮的邊界問題

單說實現,乞丐版可以依賴JSON的parsestringify做到,當然面試官肯定不會滿足於此,一定會追問你如果自己實現怎麼辦,所以後面我就聊了下自己如何實現一個簡單的深拷貝(只針對物件{}和陣列),以及實現需要考慮的邊界問題。

先說邊界問題,假設我們要實現一個深拷貝函式deepCopy,因為前面說了深拷貝是針對引用資料來說的,第一點,傳進來的引數一定是得檢驗資料資料型別,若是基礎型別就沒拷貝的必要了。第二點,物件的屬性值也可能是一個物件,因此在複製時還得對值進行判斷,若仍然是物件,我們得遞迴。第三點,我們拷貝物件自然是希望拷貝物件自身屬性,而對於非自身的繼承屬性我們得過濾掉,那麼我們來實現一個簡單的深拷貝:

// 只針對{}與陣列的簡單實現
function deepCopy(source) {
    function isObject(o) {
        // 此實現只針對陣列與{},由於null的typeof型別也是object,特做過濾
        return typeof o === 'object' && o !== null;
    };
    // 如果引數不是物件{}或陣列直接返回
    if (!isObject(source)) {
        return source;
    };
    // 建立一個新物件,是陣列就建立空陣列,反之空物件
    let target = Array.isArray(source) ? [] : {};
    // 遍歷源物件,進行拷貝
    for (key in source) {
        // 判斷當前屬性是否是自身屬性
        if (source.hasOwnProperty(key)) {
            // 判斷當前屬性的值是否仍然是物件
            if (isObject(source[key])) {
                target[key] = deepCopy(source[key])
            } else {
                target[key] = source[key];
            };
        };
    };
    return target;
};

面:ES6瞭解嗎?說下你熟悉的特性,說三條就好。

這個比較簡單,我首先說了下let const,關聯性的介紹了塊級作用域,暫時性死域,const值是引用物件時不能修改的是引用地址,之後聊了下箭頭函式與this,之後說了promise,三個狀態,all與race的區別之類的,大家挑自己熟悉的去說,能扯的詳細更好。

面:做過哪些優化工作?

這個說起來的方面就多了,比如檔案下載方法的優化,對於每個頁面儘可能做到按需載入,減少非必要檔案的下載,還可以使用http快取,檔案MD5戳減少檔案下載頻率;程式碼層面的優化,比如元件封裝,常用API封裝,減少程式碼量提升程式碼複用性;使用友好方面優化,比如圖片懶載入,onloading管理等等。

一面大致問了這些問題,面試官對於我的回答還算滿意,初面給我的評價是知識體系很系統化,思考問題也很全面,所以週四下午約了現場複試,複試兩個面試官排隊問,那麼下面是複試相關問題:

面:看你專案重構做的比較多,說下重構做了哪些工作

對於這個問題我開始詳細說了下專案結構調整,因為我當時重構並不是在原有基礎上改寫程式碼,而是對整個專案進行了從零開始的重寫,包括專案結構調整,由最初一個總應用APP檔案管理了所有頁面的service,元件相關注冊,改為APP檔案只負責註冊,單頁面分別管理自身所需依賴檔案。

這麼做的的目的主要還是按需載入,A頁面用到什麼便註冊什麼,頁面渲染時可以減少不必要的檔案下載。其餘檔案更便於管理,哪個service為誰服務一路瞭然。

之後介紹了元件封裝之類的相關重構工作,以及釋出工具配置等相關的。

面:能說下你元件封裝的思路嗎?

第一步確認需求,在瞭解需求的情況下繪製流程圖,再以流程圖與上級確認需求是否符合,可行的話之後就是看著圖寫程式碼的事情了。

面:那你怎麼保證元件重構封裝後還能滿足之前的業務需求,舉個例子,假設之前元件是滿足ABC三種情況的,但因為需求文件疏忽,或者你自己流程圖的疏忽忽略了B,你該怎麼彌補這個問題?

老實說,我一是沒遇到過這種問題,二是實在沒get到他具體想問的點在哪....後面回答也不是很好,所以直接徵求面試官合理的做法了。面試官說這個問題解決方案很多,比如可以在元件實現後可進行單元測試,這樣就能提前知道B不符合情況了,之後修改元件邏輯的同時完善需求文件。我聽完人都傻了...我一直以為他想問的是元件設計方面的問題,比如如何保證元件拓展性,所以當時回答的很失敗了。

面:你在元件裡有提到loading的實現,如果一個請求很快就結束了,你怎麼處理的?

因為我的loading在請求攔截處統一管理,request階段啟動,response階段關閉,由於請求都是同步,非同步是響應階段,一個頁面總是會存在多個請求的情況,所以開啟loading只會有一次,且開啟loading是由定時器管理,比如2S後才會啟用loading,如果一個網路請求非常快,則會將啟動loading的定時器清除。而針對多個請求,會用陣列裝在每個請求資訊,完成一個請求對應清除陣列中該請求資訊,陣列被清空時自然說明請求全部完成,則關閉loading。

面:你說在請求攔截中做了部分網路錯誤處理,那如果一個請求超時了怎麼辦?

針對於請求超時,可以通過retry來實現自動發起再次請求。隨後面試官又問retry需要注意什麼,這裡我就說了retry需要設定最大retry次數,避免無限請求,其次retry之間也需要設定間隔時間。

面:那你是對整個專案所有請求都retry了嗎,如果有些請求我不想retry怎麼辦?

這裡我實際的做法是在專案配置檔案中,專門有一個數組裝了需要retry的介面資訊,如果一個介面沒被包含,就不會執行retry,類似於多了一次條件判斷。

面:你前面說retry是根據code 504來做的,如果有些請求,後臺沒返回,也就是沒有respone,你怎麼做後續處理?

這個我當時答的也不是很好,其實不管後臺有沒有返回,前端都是可以拿到反饋資訊的,哪怕我們請求一個不存在的介面,也會得到介面404的錯誤反饋,所以針對反饋的錯誤資訊去做處理,給出提示或者其它。

面:能說下你做的多貨幣功能嗎?

這個我就大致介紹了多貨幣指令的實現,當貨幣在美元與人民幣之間切換時,會觸發指令邏輯,根據後臺提前返回的匯率進行計算,其實我說到這我就猜到會問精度問題了,但是這塊我之前沒詳細瞭解過。

面:那你前端是如何保證計算後的精度的?比如我們都知道0.1+0.2!=0.3,你說下為什麼,如何在計算中解決。

這個我回答的也不是很好,關於0.1+0.2這個問題之前有看,但是時間太久記憶模糊了,只記得是精度丟失,所以沒能很系統的去解釋。

準確來說js中的數字都採用了IEEE 754標準的64位雙精度浮點數,而對於64位的浮點數在記憶體中表示時分為了三部分,第0位符號位(佔1位,用s表示),表示這個數字是正數還是負數,0為正,1表示負數。第1到11位為指數部分(佔11位,用e表示),表示這個數值的大小,第12到63位為尾數部分(佔52位,用m表示),決定數值的精度,大致如圖:

我們知道計算機中數字都是按二進位制進行儲存的,所以第一步得將十進位制數值轉為二進位制,需要說明的是十進位制整數轉二進位制方法為除2取餘,按倒序排列,而十進位制小數轉二進位制方法為乘2取整,按順序排列啥意思?我們現在就以0.1為例,0.1整數部分為0,0/2餘0,所以先得到一個0.。我們再看小數部分,如下:

0.1 * 2 = 0.2 //取整數0,第一位,0.2參與下次計算
0.2 * 2 = 0.4 //取整數0,第二位,0.4參與下次計算,
0.4 * 2 = 0.8 //取整數0,第三位,0.8參與下次計算
0.8 * 2 = 1.6 //取整數1,第四位,0.6參與下次計算	標記1
0.6 * 2 = 1.2 //取整數1,第五位,0.2參與下次計算
0.2 * 2 = 0.4 //取整數0,第六位,0.4參與下次計算
0.4 * 2 = 0.8 //取整數0,第七位,0.8參與下次計算
0.8 * 2 = 1.6 //取整數1,第八位,0.6參與下次計算  標記2
0.6 * 2 = 1.2 //取整數1,第九位,0.2參與下次計算

請觀察上述計算,在標記1到標記2的位置,再往後其實已經進入了一個迴圈了,由於計算永遠得不到小數部分為0的情況,所以其實進入了一個無限的計算迴圈,結合前面得到0.,組合而成就是0.0 0011 0011 0011...(0011迴圈)

計算機中記憶體是有限的,所以並不可能去儲存一個無限長的數值,因此計算機在某個精度點自然就捨棄了後面的數字,這個精度點自然就是我們前面說的52位尾數了。所以二進位制轉換完成,是還會再按IEEE 754標準再轉一次,轉換公式就不說了,直接給結果:

// 0.1
e = -4;
m = 1.1001100110011001100110011001100110011001100110011010 (52位)

而0.2轉換同樣會存在這樣的問題,所以兩個都丟失了精度的數值計算後還原成十進位制,自然得到的不是0.3。具體推算可以閱讀這篇文章詳解js中0.1+0.2!=0.3

當然不是所有浮點數都有誤差,二進位制其實能精確地表示位數有限且分母是2的倍數的小數,比如0.5在計算機內部就沒有舍入誤差,所以0.5 + 0.5 === 1。

那麼我們怎麼比較0.1+0.2===0.3呢?這裡其實可以借用ES6中Number的新屬性EPSILON,它表示 1 與Number可表示的大於 1 的最小的浮點數之間的差值,所以正確的比較方式為:

 Math.abs(0.1 + 0.2 - 0.3) <= Number.EPSILON;// true

當然,關於金額數值的計算,一般都是交給後端計算,如果前端算了其實後端也要算一次做核對,所以總體來說後端一定要算。主要原因是一方面是前端計算存在浮點數問題,處理起來存在一定成本,二是前端可能存在改單情況,存在一定金額計算風險。

當然前端如果說真要算怎麼辦,這裡可以借用成熟的類庫來解決,比如math.js

面:能說下多語言怎麼做的嗎?

多語言用了angularjs的ng-translate指令,考慮大家都沒用過,這裡就不細說了。

面:能說下你這個sentry是做什麼的嗎?

sentry是一個前端錯誤監控系統,這裡我主要是基於sentry搭建了一個前端異常監控服務,用於抓取前端http請求異常,目的是在使用者使用過程中出現請求錯誤時,通過對原錯誤資訊進行加工,提取使用者資訊,出錯頁面,請求地址,引數,響應資料等相關資訊並上傳日誌,開發者可通過郵件快速定位錯誤,達到遠端排錯的目的。

面:你覺得你工作中遇到最有難度或者最有挑戰性的事情是什麼?比如花了很久時間解決的事。

其實我在面試前就想到如果被人問到這個問題怎麼辦,但是我覺得自己專案真的亮點不足,涉及技術點不深,結果這次真遇到了,然後我直說了沒有- -,因為專案太簡單了(在後續聊天中我找到了下次可以回答的答案)。

面:XMLHttpRequest物件瞭解嗎?

我們熟知的ajax在不重新整理頁面情況下,非同步更新頁面部分資料,其實底層依賴的就是XMLHttpRequest物件,所以我圍繞ajax使用,大致說了建立xml物件=>利用open設定請求方式與請求地址以及是否非同步=>利用send傳送請求=>設定監聽函式onreadystatechange函式=>處理響應資料五個過程,介紹了readyState不同狀態的具體含義。

面:fetch瞭解嗎?跟ajax有什麼區別?

說來慚愧,這真是我第一次聽到fetch,所以面試官也很詫異,說這麼常用的東西你都不知道....仔細回想這三年,第一年用JQ,後兩年用angularjs內建的http請求,周圍也沒有一個人給我提起過這個詞,那麼這裡簡單整理下。

首先fetch是原生js的實現方案,與ajax並無關係,你可以把它當成XMLHttpRequest的一種更理想的替代方案,fetch語法如下:

fetch(input[, init])

其中input可以是獲取資源的地址,或者是一個request物件。而init則是一個包含所有請求設定的物件,比如請求方法,headers請求頭資訊以及可以使用mode:cors達到跨域效果等等,來看個簡單的例子:

fetch('http://example.com/movies.json')
    .then(function (response) {
        return response.json();
    })
    .then(function (myJson) {
        console.log(myJson);
    });

fetch不管請求成功失敗,都會返回一個promise,我們可以使用then方法處理回撥。

ajax對比,fetch主要有三點不同:

  • 當接收一個表示錯誤的http狀態碼時,fetchpromise不會被標記為rejecte,反而是resolve(但是會將resolve的ok屬性設為false),只有網路故障,請求被阻止這種情況才會被標記reject
  • fetch可以接受跨域cookies,你也可以使用fetch建立跨域請求。
  • fetch預設不會發送cookies,除非我們通過前面的init物件修改了初始化配置。

更多fetch還是推薦大家閱讀MDN使用 Fetch詞條

面:如果讓你對比angularjs和vue,你覺得angularjs優勢在哪?

這個問題就挺頭大了,人人都知道angularjs由於髒檢測機制等歷史遺留問題,官方都放棄了此版本的更新而新開了angular版本,而vue在設計初期借鑑了angularjs一些思想,整體上來說vue是更具優勢的,所以想來想去我回答的是angularjs優勢體現在元件化,mvc思想先驅的角色扮演。

面:那vue相對angularjs你覺得有哪些優勢呢?

這個就好解釋一點了,其實對於我的開發體驗來說,最大的感觸還是angularjs的髒檢測效能問題,angularjs髒檢測原理其實是由digest方法觸發,預設從頂層作用域開始深度遍歷,依次訪問每個作用域中的watcher陣列中的每個資料,一一對比是否發生變化,所以如果作用域巢狀多,監聽資料多,每次更新開銷都是巨大的,但相對這一點,vue使用基於依賴追蹤的觀察系統,若一條資料變化只會更新本資料,成本會小很多。其次針對元件通訊,angularjs官方並未友好提供兄弟通訊的方式,但是在vue中借用vuex等會便捷很多。

面:有了解過設計模式嗎?

面:有推進公司專案,或者技術研究嗎?

面:最近做過哪些技術分享?

這幾個問題就統一說下了,設計模式我之前有讀過JavaScript設計模式相關資料,不過實踐能力較弱,並沒到融會貫通的地步,推進專案我說的是沒有,小嘍嘍一個,技術分享自然是部落格了,說了最近就是寫一些面試覆盤總結。

大致到這裡就算面試結束了,兩個面試官說要討論下結果,順便看下我的部落格。大約等了10來分鐘,得到的反饋是專案無亮點,軟體思維薄弱,目前公司只有三個崗位,1個高階2個初級,並沒有適合我的位置。

其實我面完感受也不是很好,我深知自己專案難度不高,沒太多亮點,面對深挖專案的面試我真的毫無優勢,所以結果也算預料之內了,所以我急忙就問了面試官前面我回答的不是很好的問題,面試官笑著說還想著討點面經啊,然後就又跟我閒聊了十來分鐘。

我:我深知自己專案無亮點,難度並不高,所以被問到你覺得工作你做過覺得最有難度的事情時我怎麼回答?

面試官說,這個問題其實並不侷限於程式碼層面,當然如果你確實有花了很大的功夫解決了一個非常困難的問題,自然印象分會更高,如果確實沒有,就可以拓展到工作其它方面,比如你推進了專案程序,在期間付出了很多,扮演了一個核心角色,這樣也能體現出你的溝通,協同,領導能力等等。我聽到這才恍然大悟,之前確實有專案從UI設計初期參與討論,跟後臺討論定義定義等等,是有過推進專案的經歷。

我:對於我之後的面試,或者職業發展能不能給我一些建議?

面試官說看過了我的部落格,面試總結寫的非常到位,之前初面就是覺得我基礎回答非常出色,所以雖然我不會react但也很感興趣,對我的額外評價是,基礎紮實,學習能力很強,自律,讓我一定要把寫部落格的習慣堅持下去,對於後期發展,讓我一定要在某個領域專精,這是到高階必經之路,之後轉架構或者全棧那就是橫向拓展知識面的事情了。

在後面的閒聊中,我才知道他們公司招聘確實算很嚴格了,面試了200多人,只發了2個offer,其中一個去了大廠沒來,最後面試官直言確實很欣賞我,只是公司職位卡的太死,也算是我最後的一點安慰了。

叄 ❀ 某不錯的公司

下午面完了上家複試,晚上六點等來了第二家公司的視訊初面,大家印象裡的初面可能都在半小時之內,結果我這個初面面了一個半小時....也算是讓我印象深刻了,那麼直接開始。

面:說下你哪個專案最有亮點?

哪個專案最有亮點,做過哪些優化,這些真的是高頻問題了,所以大家一定要總結自己專案的出色點,這個真的是必問,我的回答自然是自己負責的重構專案,說了下優化方面的事情。

面:說下你通用服務,比如指令怎麼劃分?

我前面說過了我的註冊由早期統一註冊改為了按需載入,所以在重構初期,是對於service,指令做了對應頁面、功能的一個初步劃分,比如購物車相關的介面API會統一放在購物車的service中,而關於註冊方面,如果一個指令,服務是整個專案全域性使用的,那就全域性註冊,如果一個服務是單個頁面使用,那就按需載入去註冊。

面:我看到你專案中有個錯誤code的功能,能說下嗎?

內部的一個小功能了,之前的覆盤也總結過,大家看了也沒啥幫助,就不細說了。

面:瞭解http快取嗎,能不能說下?

首先http1時期,http快取靠的是絕對過期時間Expires,也就是第一次請求後,服務端給你一個絕對時間,時間沒到你就不能請求直接用快取,這樣肯定就很不人性化了。之後http1.1就可以使用Last-Modified來比較檔案的修改時間,這也其實也會存在修改時間變了,檔案內容沒變的情況,所以還是存在一定問題。而最後就是檔案內容唯一標識Etag,只要這個對不上,說明檔案變化就可以重新下載更新了。除此之外我也簡單說了下協商性快取與強快取的概念。

面:不知道你有沒有了解過,同樣都是快取,但有時候狀態碼不是304,比如快取有額外說明from memory cache,有時候是from disk cache,你知道它們區別嗎?

我知道面試官是想問記憶體快取與磁碟快取,真沒想到會問這麼深...瀏覽器快取其實滿足三級快取原理,即走快取的情況,先看記憶體中有沒有快取,有就取記憶體;如果記憶體中沒有,就看磁碟中有沒有,有就取磁碟,如果磁碟都沒有,那就只有再次發起網路請求,將載入的資源再次快取到記憶體和磁碟中。

其次,memory與disk儲存方式以及檔案型別也不同,具體如下:

  • 200 form memory cache:不發起請求,從記憶體中讀取快取檔案,但如果關閉瀏覽器資料將會被釋放,下次再開啟頁面就得重新請求了,一般指令碼,字型,圖片都存在記憶體中。
  • 200 form disk cache:不發起請求,從磁碟中讀取快取,關閉瀏覽器後資料還會存在,所以下次再開啟相同頁面還是走快取,磁碟中一般快取類似css非指令碼檔案。

面:http2瞭解嗎?

這個因為我也未真正用過,其實都是站在理論層面,所以說了下http2的新增特性:

  • http2支援新的二進位制格式傳輸檔案
  • http2支援通訊雙方快取header表資訊,這樣在下次請求能避免重複資料的傳輸
  • http2支援服務端主動推送資料給客戶端。

面:跨域瞭解嗎?說三種常見的解決方案

這個也是常考題,不過方案除了nginx反向代理,JSONP,window.name結合iframe外,前面我們提到的fetch也是一種不錯的方案。

面:Virtual DOM瞭解嗎?

因為我並未真正實戰過vue和react,這塊的理解是很薄弱的,所以就直說不是很瞭解了,關於虛擬DOM可以深入閱讀你不知道的Virtual DOM系列文章。

在面試官確信我不知道虛擬dom之後,後面的問題就開始賣坑來考我了。

面:假設現在有1000條資料的長列表,說下不用分頁你怎麼優化載入?

不使用分頁,我能想到的自然就是下拉預載入了,比如首先載入10條資料,使用者下拉滾動條快到地步時預載入後10條。

面:你這樣做的話,就是一邊下拉一邊載入,也行,那如果我要一開始拉到滾動條中間位置,直接看到500左右的資料你怎麼辦?

我大致想了想,前面的方案其實是沒有一開始展示總高度的,現在要一開始拉到中間位置,自然要把總高度一開始就計算出來。我就說假設每條資料渲染出來都有固定高度,我先計算出1000條資料需要的總高度,然後根據你拉到中間位置,我是可以獲取滾動條距離頂端的高度的,這樣我就能計算出需要展示到第幾百條資料了。

面:這樣做是可以,但是你現在還是渲染出了500條資料,dom也還是很多,有沒有辦法再優化,給你點提示,回收dom。

問到這我就懵逼了,如果我把前面的資料,也就是超出視窗的資料回收,是可以保證視窗內只展示20條資料,那如果我回滾怎麼辦,我還得把現在的dom回收掉,不斷去取之前的資料再渲染,這操作dom也太複雜了。然後面試官又對我說,回收dom也是我給你留的坑,你前面說了得計算總高度,一旦dom回收,頁面高度也會坍塌,所以想來想去沒有很好的思路。

面試官笑了笑,說這個其實可以用虛擬dom來做,所以到這我算明白麵試官用意,給一個虛擬dom實際場景,在我未掌握該知識的情況下,考我如何用已知知識解決這個問題,第二給我引出虛擬dom的實用性,所以整體我覺得這樣問真的是非常有價值的。

在後面的講解中,我大致理解的做法是這樣,當然不一定準確:

面試官的意思是,將我們的瀏覽器視窗理解成一個畫板,隨著下來,從虛擬dom中取到一幅幅畫,貼在我們的畫板上,由於是絕對定位貼上去的,脫離了文件流,所以不存在高度坍塌的問題;總之到這我就是驚了!

面:你怎麼學習一個新知識?

我的學習習慣是先通讀文件,比如vue,我先把整體知識框架搭建起來,這樣好處是能在深入學習之前就知道部分知識的關聯性,整體架構建好了,自然可以根據前輩的經驗,對於重點難點進行深入學習。學習對我而言,最重要的是避免只見樹木不見森林。

面:你怎麼平衡知識的新與舊?

我覺得新與舊是一個相對的概念,無法輕易下結論誰好誰壞。舊的東西自然更穩定性,風險性要小,但是思想上可能不如新知識更具便捷性與創新,但也不提倡死板守舊,新的思想總是可以接納的。所以以專案重構而言,還是得考慮成本,風險等多方面因素。

面:一個樹形結構資料,不用遞迴,如何用遍歷去訪問它?

因為說到樹本能會想到遞迴吧,大致給個類似的題目大家看看(題目來源在Javascript中,如何才能不使用遞迴來遍歷一個樹狀結構?):

var nodes = {
    value: 1,
    children: [{
            value: 2,
            children: [{
                    value: 4,
                    children: []
                },
                {
                    value: 3,
                    children: []
                },
            ]
        },
        {
            value: 5,
            children: []
        },
    ]
};

如果用遞迴,就是判斷nodes的children還有沒有子元素,有就繼續遞迴呼叫自身方法

function sum(nodes) {
    if (nodes.children.length === 0) {
        return node.value;
    }
    return nodes.children.reduce((pre, cur) => {
        return pre + sum(cur);
    }, nodes.value)
};

如果不準用遞迴,就得借用其它資料結構,比如佇列(先進先出):

function bfs(nodes) {
    const queue = []
    let sum = 0
    queue.push(nodes)
    while (queue.length) {
        const curNode = queue.pop();
        sum += curNode.value;
        curNode.children.forEach(ele => queue.push(ele));
    };
    return sum;
};

當然這不是我答得,只是給出了一個類似的題目,我演算法還是小弱雞。

大致到這,面試就結束了,因為跟面試官聊得還可以,果不其然,第二天收到了複試邀請,在下週二,說實話吧....初面就這麼深入,複試是技術總監面,我感覺有點懸,具體面了再說。

肆 ❀ 逃離湖泊投身大海的魚

說下這周面試的感受,我大概就是逃離湖泊投身大海的魚,以前在上家公司,同事覺得我見多識廣,部落格園朋友覺得我多厲害,其實幾番面試下來,我自己深知自己與大佬之間的差距,雖然一開始我就不覺得自己有啥厲害的。技術深度,解決問題的能力,軟體設計等等,但這些都不是一時半會能培養起來的,仍需要工作的積累,這也是我為何對於下家公司技術團隊比較執著的原因。

每天壓力還是很大,面試就像一場考試,不管考得好不好,考完了總是覺得鬆了口氣,像是可以逃避了一樣,可是終究逃不掉,我還是得繼續安排之後的面試,以及不斷的面試。而回想當初離職的決定,我現在反而覺得更正確了,一開始就知道這次會很難,再難這次也要挺過去。

這篇文章花了我一個週末的時間(昨天寫到了凌晨2點...),寫到這居然已經有九千字了,大概是我所有部落格裡最長的一篇了。我也希望能儘快找到滿意的工作,結束這個系列的更新,畢竟誰又願意頂著壓力天天寫面試分享呢,真正的難受也只有自己心裡清楚。

但不管怎麼說,我還是會堅持,有面試就一定會總結,只要自己不認輸,那我永遠就沒有輸,請繼續加油!本文結束。

伍 ❀ 參考

from memory cache與from disk cache

徹底弄懂:0.1+0.2 != 0.3

為什麼0.1+0.2不等於0.3

JavaScript 浮點數運算的精度問題

為什麼0.1+0.2不等於0.3?原來程式語言是這麼算的