
1. 試卷全景與考察定位1.1 為什么一份2018年的筆試題現(xiàn)在還值得刷先交代一下背景。歡聚時代就是大家熟知的YY2018年這波校招筆試在當(dāng)年的前端圈里流傳度還挺高原因是它的考察覆蓋面比較全且題型設(shè)計踩中了不少前端er的痛處。網(wǎng)絡(luò)上流傳的版本有不少殘缺很多是候選人考完憑記憶拼出來的原卷已經(jīng)找不到了。我結(jié)合自己做前端面試官的經(jīng)驗和帶應(yīng)屆生的復(fù)盤筆記以當(dāng)年的A卷考綱為骨架把核心考點逐一拆開補上解題思路和避坑點。先說結(jié)論這份試卷雖然出題年份停在2018但它考察的知識底座到現(xiàn)在都沒有過時。HTML、CSS、JavaScript三件套的底層功力瀏覽器的運行原理經(jīng)典的算法手寫題依舊是大廠校招前端筆試題的固定組成部分。唯一變化的是近幾年的試卷里越來越多地加入了TypeScript、工程化、框架運行時原理、微前端等內(nèi)容但基礎(chǔ)模塊的權(quán)重并沒有降低。我之所以推薦大家拿這份題來練是因為它比較適合用來做“知識體檢”。一份卷子做完哪些地方卡殼哪些地方能秒答基本能反映出你前端基礎(chǔ)的真實水位。而且筆試和面試不太一樣筆試考的是你在沒有提示、沒有交流的情況下能不能獨立還原出一個正確的結(jié)論。這個能力恰恰是很多培訓(xùn)班出身、習(xí)慣看視頻邊看邊敲的同學(xué)最容易缺的。對于正在準(zhǔn)備校招的應(yīng)屆同學(xué)我的建議是不要太糾結(jié)“這是哪一年的題”而要把注意力放在“這道題到底在測什么能力”上。下面我按當(dāng)年A卷的模塊結(jié)構(gòu)把每一類題目的考察邏輯和準(zhǔn)備方向捋一遍。1.2 試卷結(jié)構(gòu)復(fù)盤與各模塊權(quán)重雖然原始卷面已經(jīng)不在我手上但根據(jù)當(dāng)時參加筆試的同學(xué)反饋整個A卷基本覆蓋了以下六類內(nèi)容。模塊常見題型大致權(quán)重難度JavaScript基礎(chǔ)輸出題、原型/閉包/作用域30%中瀏覽器與網(wǎng)絡(luò)事件循環(huán)、渲染機制、緩存20%中HTML/CSS布局手寫布局、盒模型、居中15%中低算法與編程題手寫數(shù)組/字符串處理、排序20%中高代碼閱讀與調(diào)試給出代碼判斷輸出或bug10%中綜合與擴展前端工程化、性能優(yōu)化5%中高這個比例在今天依然有參考價值。JavaScript永遠是前端筆試的核心這類題不太需要背主要靠對語言機制的理解。如果在JS上栽了跟頭后面再扎實的框架經(jīng)驗也難救回來因為筆試階段通常不考框架考的就是你沒框架時還能不能寫出可靠的代碼。1.3 筆試答題的核心姿勢線上筆試和面試不一樣面試官不在你旁邊你思考的過程沒人看最終提交的答案就是一切。所以答題順序很關(guān)鍵我的習(xí)慣是先掃一遍全卷把會做的、能拿分的題先標(biāo)記出來優(yōu)先死磕會做的再回頭啃難題。不要在一道題上死磕超過15分鐘尤其是后面的手寫算法題往往分值更高、性價比更大。另外手寫代碼題不要只寫代碼可以在注釋里寫清思路?,F(xiàn)在不少筆試系統(tǒng)支持運行和判題但即便不支持注釋能幫閱卷人理解你的邏輯也方便后續(xù)面試環(huán)節(jié)中面試官拿著你的答卷問細節(jié)。我在后來做面試官時最反感的就是答案只給一個函數(shù)簽名主體邏輯全空著那基本等于沒做。2. JavaScript基礎(chǔ)題拆解2.1 作用域、閉包與變量提升這份卷子里JS部分的開場題通常是讀代碼寫輸出。翻來覆去考的就是幾個點var/let/const的區(qū)別、變量提升、函數(shù)提升、閉包、this指向、原型鏈。我用當(dāng)年流傳很廣的一道經(jīng)典變形題來演示。var a []; for (var i 0; i 3; i) { a[i] function () { console.log(i); }; } a[0](); a[1](); a[2]();很多人第一次做這道題會以為結(jié)果是0、1、2。但真正跑出來是3、3、3。原因在于var i聲明的變量是函數(shù)作用域整個循環(huán)共享同一個i函數(shù)被調(diào)用時循環(huán)早已結(jié)束i已經(jīng)變成3。如果把var i改成let i結(jié)果就變成0、1、2。因為let是塊級作用域for循環(huán)的每次迭代都會創(chuàng)建一個新的綁定。這種題表面考的是作用域?qū)嶋H上背后是“執(zhí)行上下文”和“閉包捕獲變量”的機制。我在實際閱卷時發(fā)現(xiàn)很多同學(xué)知道該把var改成let但要他說清楚為什么就支支吾吾了。這里有一個比較直觀的理解方式你可以把閉包想象成一個小房間房間里的窗戶可以看到外面的變量。用var時所有房間共用同一扇窗戶窗外只有一臺數(shù)字顯示器用let時每次循環(huán)都新建一臺顯示器每個房間看到的是自己對應(yīng)的那一臺。準(zhǔn)備這類題時建議動筆把執(zhí)行上下文的內(nèi)存分布畫一遍。面試官后續(xù)大概率會追問“閉包會造成內(nèi)存泄漏嗎”“怎么避免”如果你能順手答出“引用計數(shù)、標(biāo)記清除、及時釋放不需要的引用”印象分會明顯不一樣。2.2 this指向與箭頭函數(shù)this指向幾乎是必考點。常見出題形式還是給一段代碼讓候選人判斷輸出。核心規(guī)律就那么幾條按優(yōu)先級排序new關(guān)鍵字調(diào)用this指向新創(chuàng)建的實例call、apply、bind可以顯式指定this方法調(diào)用誰調(diào)用指向誰普通函數(shù)調(diào)用非嚴(yán)格模式下指向全局對象嚴(yán)格模式下是undefined箭頭函數(shù)沒有自己的this沿用定義時外層作用域的this真題里最喜歡考的是第3條和第4條結(jié)合var name window; var obj { name: obj, getName: function () { return this.name; }, }; var fn obj.getName; console.log(fn()); console.log(obj.getName());這里obj.getName()輸出obj很好理解。fn()是存了引用之后直接調(diào)用相當(dāng)于普通函數(shù)調(diào)用在瀏覽器環(huán)境下this指向window輸出window。很多人栽在第4條因為沒意識到“取方法再調(diào)用”會丟失this。順帶說一個容易混淆的點回調(diào)函數(shù)里寫this和箭頭函數(shù)里寫this是兩回事。事件監(jiān)聽器、setTimeout回調(diào)里的普通函數(shù)this指向由調(diào)用方式?jīng)Q定而箭頭函數(shù)則始終指向定義所在作用域的this。理解了這點再遇到“Vue methods里為什么箭頭函數(shù)不推薦”這類擴展題就能順藤摸瓜答出來。2.3 原型鏈與繼承原型鏈?zhǔn)荍S模塊里區(qū)分度很高的一塊。2018年的A卷給了一段構(gòu)造函數(shù)和實例的代碼要求判斷obj.__proto__ Constructor.prototype這類表達式的真假。基本功需要掌握到以下程度function Animal(name) { this.name name; } Animal.prototype.say function () { console.log(this.name); }; var dog new Animal(旺財);此時dog.__proto__ Animal.prototype、Animal.prototype.constructor Animal、dog instanceof Animal這幾個表達式的結(jié)果都要能一口說出來。再深一層Animal.__proto__ Function.prototype因為Animal本身就是Function構(gòu)造出來的實例Animal.prototype.__proto__ Object.prototype因為原型鏈最終會收斂到Object。筆試中關(guān)于繼承的題一般會讓你手寫ES5的寄生組合式繼承或者讓你指出幾種常見繼承方式的優(yōu)缺點。我見過大量答案是把“原型鏈繼承”“構(gòu)造函數(shù)繼承”“組合繼承”混在一起說不清楚。要區(qū)分的話記住一個判斷標(biāo)準(zhǔn)子類的實例是否共享父類原型上的引用類型屬性、父類構(gòu)造函數(shù)是否被調(diào)用了一次還是兩次。組合繼承有個小問題就是父類構(gòu)造函數(shù)被調(diào)用了兩次而寄生組合式繼承就是為了解決這個問題的。3. 瀏覽器與網(wǎng)絡(luò)基礎(chǔ)題解析3.1 事件循環(huán)從經(jīng)典輸出題到微任務(wù)宏任務(wù)瀏覽器部分的題目第一梯隊就是事件循環(huán)。當(dāng)年那套卷里有一道流傳甚廣的題大概是這樣的變形console.log(1); setTimeout(function () { console.log(2); }, 0); Promise.resolve().then(function () { console.log(3); }); console.log(4);執(zhí)行結(jié)果是1、4、3、2。很多人不理解為什么Promise的回調(diào)比setTimeout還早。原因是Promise的then回調(diào)屬于微任務(wù)microtasksetTimeout回調(diào)屬于宏任務(wù)macrotask。當(dāng)前宏任務(wù)執(zhí)行完后JS引擎會先把微任務(wù)隊列清空再取下一個宏任務(wù)執(zhí)行。以Chrome的渲染主線程為例可以簡化理解為每一次從宏任務(wù)隊列取一個任務(wù)執(zhí)行過程中如果產(chǎn)生了微任務(wù)全部塞進微任務(wù)隊列當(dāng)前宏任務(wù)結(jié)束后按順序清空微任務(wù)隊列然后執(zhí)行渲染相關(guān)任務(wù)最后再取下一個宏任務(wù)。我第一次帶新人時經(jīng)常用“食堂打飯”來比喻宏任務(wù)窗口是普通窗口微任務(wù)是窗口旁邊的小賣部你剛打好飯執(zhí)行完一個宏任務(wù)第一件事是先跑小賣部把酸奶買了清空微任務(wù)然后再去下一個普通窗口打飯。關(guān)于Promise本身A卷里還有一類改錯題比如要求寫出Promise.all的替代實現(xiàn)。給出三個異步請求要求全部成功后在頁面上展示合并后的數(shù)據(jù)。核心就是Promise.all的用法以及失敗時用catch兜底。這道題看著不難但實際手寫時很多同學(xué)會忘記Promise.all的入?yún)⒈仨毷强傻鷮ο笠踩菀淄浱幚怼皞魅肟諗?shù)組”時的邊界情況。準(zhǔn)備這類題時建議把Promise的靜態(tài)方法全過一遍resolve、reject、all、race、allSettled、any每個都要知道入?yún)⒑头祷刂怠?.2 從輸入URL到頁面展示必背主鏈路這部分很少以問答題形式出現(xiàn)在筆試卷里更多是在面試環(huán)節(jié)追問但筆試也會通過選擇或簡答題間接考察。最典型的簡答題是“在瀏覽器地址欄輸入一個網(wǎng)址到頁面渲染完成中間發(fā)生了什么?!蓖暾溌房梢圆鸪梢韵聨锥蜠NS解析把域名解析成IP地址中間會經(jīng)過瀏覽器緩存、系統(tǒng)緩存、路由器緩存、根域名服務(wù)器等層級建立TCP連接三報文握手如果用的是HTTPS建立TLS握手協(xié)商加密套件和密鑰發(fā)送HTTP請求服務(wù)器返回響應(yīng)瀏覽器拿到HTML后解析HTML生成DOM樹解析CSS生成CSSOM樹DOM和CSSOM合并成渲染樹布局計算各元素的位置和尺寸繪制到頁面合成圖層這段內(nèi)容雖然長但它其實是瀏覽器原理里最好背的。準(zhǔn)備方向可以結(jié)合性能優(yōu)化來理解為什么CSS要放頭部、JS要放尾部再配合defer和async為什么樣式的層級不宜過深這些問題的答案都能在上面這條鏈路里找到依據(jù)。3.3 HTTP緩存與前端網(wǎng)絡(luò)優(yōu)化雖然2018年的卷子對HTTP緩存考得沒有現(xiàn)在這么重但基礎(chǔ)概念還是要過一遍。強制緩存和協(xié)商緩存這對概念幾乎每家公司的筆試都會碰。強緩存相關(guān)響應(yīng)頭是Cache-Control常用值有max-age、no-cache、no-store優(yōu)先級高于老牌的Expires。協(xié)商緩存相關(guān)響應(yīng)頭是Last-Modified/If-Modified-Since和ETag/If-None-Match。筆試??嫉膯柎鹗菑娋彺婷袝r返回什么狀態(tài)碼答案是200并且會顯示from disk cache或from memory cache。協(xié)商緩存命中時返回304。我見過很多人把這兩個概念攪在一起其實只要抓住一點強緩存不發(fā)請求直接本地取協(xié)商緩存是發(fā)一個帶條件的請求由服務(wù)器決定使用緩存還是返回新資源。理解了這套機制再看前端工程化里的文件名指紋邏輯就通了。webpack打包時會在文件名里帶上hash本質(zhì)上就是為了讓靜態(tài)資源更新后能自然跳過強緩存命中重新走一遍協(xié)商緩存或直接拉取新文件。這是筆試?yán)锍R姷木C合題很多同學(xué)看過webpack配置但從來沒想過它為什么那樣設(shè)計。4. HTML/CSS布局與樣式問題4.1 等高布局CSS部分在筆試?yán)镆话悴粫继念}重點集中在盒模型、浮動、定位、Flex、Grid以及幾類經(jīng)典布局的手寫題上。其中手寫兩欄布局和三欄布局的概率出奇的高。三欄布局中比較推薦的做法是flex代碼量少邊界情況也少。.container { display: flex; } .left { width: 200px; } .middle { flex: 1; } .right { width: 200px; }如果要求中間欄自適應(yīng)左右固定這個方案就夠了。如果想要中間欄優(yōu)先加載傳統(tǒng)做法是圣杯布局或雙飛翼布局它們都是基于浮動和負margin實現(xiàn)的。很多同學(xué)會把這兩個布局的結(jié)構(gòu)原理弄混這里我提供一個記憶方法圣杯布局在container上加padding用相對定位移動左右兩欄雙飛翼布局在中間欄內(nèi)部再加一層用margin留出左右空間。前者是“container讓位”后者是“中間欄內(nèi)部讓位”。除了布局居中問題也是高頻中的高頻。水平垂直居中我的首選方案是flex或grid極端兼容場景下再考慮transform或絕對定位。這里有一道容易翻車的問題“不定寬高的元素怎么實現(xiàn)水平垂直居中”只要答出以下這三種方式之一并說明原理即可flex父容器display:flex; align-items:center; justify-content:centergrid父容器display:grid; place-items:center絕對定位左右設(shè)為0margin設(shè)為auto配合top/bottom設(shè)為0值得注意的是絕對定位加margin auto這種方式需要元素有明確的寬高否則內(nèi)容會塌縮。不設(shè)寬高的時候用transform方式更穩(wěn)妥。4.2 盒模型與BFC筆試題里盒模型基本是送分題但送分也總有人拿不到。關(guān)鍵在于Doctype。在標(biāo)準(zhǔn)模式下box-sizing: content-box時width只包含內(nèi)容寬度padding和border按標(biāo)準(zhǔn)疊加出去設(shè)為border-box時width包含內(nèi)容padding和控制border。怪異模式是舊IE時代的產(chǎn)物現(xiàn)在基本不用管但面試官偶爾會拿它來測試候選人是否真的理解盒模型的歷史背景。BFC看起來有點理論化但結(jié)合實際問題就很好記。BFC全稱是Block Formatting Context塊級格式化上下文。在一個BFC內(nèi)部子元素的margin不會和外部元素合并float的元素也被包含在BFC的范圍內(nèi)。實際操作上觸發(fā)BFC的方式包括overflow不為visiblefloat不為nonedisplay為inline-block、table-cell、flex等position為absolute或fixed最常見的應(yīng)用場景就是清除浮動。父容器內(nèi)的子元素都float了父容器的高度塌了給父容器加overflow: hidden它就觸發(fā)BFC子元素的浮動被包含進來高度恢復(fù)正常。明白了原理再背十個清除浮動的寫法都不如看一眼本質(zhì)。4.3 手寫輪播圖時的樣式細節(jié)2018年A卷的編程題里有一道定時輪播的小題要求實現(xiàn)圖片自動切換并且配了左右按鈕手動切換。很多人把JavaScript邏輯寫了半天卻忽略了樣式細節(jié)結(jié)果一排圖片水平排列時就出問題。我當(dāng)時給同事的建議是先用統(tǒng)一容器包住所有輪播項容器寬度設(shè)成圖片數(shù)量的百分比倍每張圖片寬度設(shè)為容器的幾分之一再利用父容器的overflow: hidden裁剪可視區(qū)域。切換時用transform的translateX移動容器而不是改left或margin這樣可以避開reflow性能更好。.carousel { overflow: hidden; } .carousel-track { display: flex; transition: transform 0.3s ease; } .carousel-slide { flex: 0 0 100%; height: 300px; background-size: cover; }對應(yīng)js邏輯大概是這樣let index 0; const track document.querySelector(.carousel-track); const total 3; function show(i) { if (i 0) { i total - 1; } else if (i total) { i 0; } index i; track.style.transform translateX(-${index * 100}%); } setInterval(() { show(index 1); }, 3000);這套代碼雖然簡單但體現(xiàn)了幾個關(guān)鍵點用transform做動畫、用flex避免float布局的麻煩、邊界處理時對負索引和越界索引做取模邏輯。這些都是前端開發(fā)中真正常用的小能力。5. 算法與手寫編程題5.1 ??际謱戭}清單筆試?yán)锏木幊填}一般不會上太難的算法重點落在數(shù)組、字符串、排序、去重、防抖節(jié)流一類的題目上。我見過的高頻手寫題按出現(xiàn)概率排序大致是這十個數(shù)組去重數(shù)組扁平化/拍平防抖與節(jié)流深拷貝手寫Promise.all字符串反轉(zhuǎn)或回文判斷排序算法基礎(chǔ)冒泡、快排查找數(shù)組最大/最小元素手寫bind/call/apply父子樹的遍歷或查找這里我把數(shù)組去重展開講一下因為它能考察的知識點非常多從Set到Map再到reduce幾種方式的復(fù)雜度都不一樣。最簡潔的寫法是[...new Set(arr)]但如果你只會這一種面試官追一句“如果要去重對象數(shù)組呢”就容易卡住。更好的方式是掌握兩層遍歷加splice的經(jīng)典寫法再在此基礎(chǔ)上給出利用對象鍵名去重或Map去重的方案。數(shù)組扁平化也一樣遞歸版本要會寫還要知道Array.prototype.flat的用法和它的默認展開層數(shù)。function flatten(arr) { return arr.reduce((prev, cur) { return prev.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []); }5.2 事件循環(huán)題目里的輸出順序除了常規(guī)算法A卷還有一種編程題是給一張頁面要求用setTimeout實現(xiàn)某個間隔執(zhí)行的效果。這類題不完全考算法而是考異步編程能力。比如要求每一秒打印遞增的數(shù)字能實現(xiàn)的方式很多。最常見的一種錯誤解法是for (var i 0; i 5; i) { setTimeout(function () { console.log(i); }, 1000); }這又回到了閉包題的坑。一秒后打印出來的不是0到4而是五個5。正確寫法是用letfor (let i 0; i 5; i) { setTimeout(() { console.log(i); }, 1000 * i); }或者用IIFE包一層再或者用bind傳參。我在筆試題的閱卷記錄里這道題的自定義錯誤率極高。如果你做出來了但又想真正弄懂建議再想想如果要求每間隔1秒打印一個數(shù)字而不是1秒后全部一起打印那么延遲時間應(yīng)該怎么寫。這才是setTimeout題型的核心。5.3 手寫防抖與節(jié)流的邊界條件防抖節(jié)流看起來簡單但真正手寫的時候很多人的代碼缺少邊界處理。以防抖為例核心邏輯是事件觸發(fā)后設(shè)置一個定時器在等待時間內(nèi)再次觸發(fā)就清掉舊定時器并重新計時只有最后一次觸發(fā)生效。function debounce(fn, delay) { let timer null; return function (...args) { if (timer) { clearTimeout(timer); } timer setTimeout(() { fn.apply(this, args); }, delay); }; }關(guān)鍵問題是this和args。很多初學(xué)者寫出來的版本在setTimeout里沒有保留this指向結(jié)果控制臺報錯或者this指向了window。正確的做法是先用fn.apply(this, args)把當(dāng)前上下文傳進去。節(jié)流則相反它的核心在于“限頻”一段時間內(nèi)最多執(zhí)行一次常見實現(xiàn)有時間戳版本和定時器版本。如果筆試時間允許我建議寫完防抖之后順便把“是否可以立即執(zhí)行一次”的版本也寫好。比如搜索框場景一般希望防抖延遲請求但按鈕點擊場景可能更希望第一次點擊立即觸發(fā)后續(xù)點擊被節(jié)流。兩個版本都練一遍面試時被追問也不慌。6. 框架與工程化啟蒙題6.1 從筆試看大廠對框架的要求2018年的校招筆試題幾乎不直接考察Vue或React的API調(diào)用因為筆試沒法驗證你真實使用框架時的能力。但會在綜合題里隱含地問你數(shù)據(jù)驅(qū)動的本質(zhì)是什么、組件通信的本質(zhì)是什么、為什么虛擬DOM能提升性能。我見過A卷流傳版本里有這樣一道半開放題“Vue中data為什么必須是一個函數(shù)”。這題放到今天依然很經(jīng)典。原因是組件可能被復(fù)用如果data直接寫成一個對象那多個實例會共享同一個數(shù)據(jù)引用一個實例改了數(shù)據(jù)所有實例都會變。寫成函數(shù)每次創(chuàng)建組件實例時都能返回一份新的數(shù)據(jù)對象。如果這套題里不出現(xiàn)類似問題面試環(huán)節(jié)也大概率會問到。所以準(zhǔn)備筆試時不要只抱著算法刷框架的底層機制起碼要能在“一句話講清楚”的粒度上回答出來。比如Vue2的響應(yīng)式原理是Object.definePropertyVue3換成了ProxyReact的setState是異步的狀態(tài)更新后組件會重新渲染渲染后可以拿到新的props和state。6.2 模塊化與打包基本盤工程化相關(guān)題在這個階段不會出得太偏但會考CommonJS和ES Module的區(qū)別。這是很多人容易模糊的點。筆試?yán)锍R姷目邮莚equire是同步加載import是靜態(tài)導(dǎo)入可以被靜態(tài)分析所以在編譯期就能做依賴分析支持tree-shaking。而CommonJS是運行時加載只能在運行時確定依賴關(guān)系。類似地webpack的構(gòu)建流程要能說出大致鏈路入口文件解析模塊依賴收集loader轉(zhuǎn)換插件處理代碼分割最終輸出bundle。如果時間緊張理解到“配置里的entry對應(yīng)入口、output對應(yīng)輸出、loader負責(zé)文件轉(zhuǎn)換、plugin負責(zé)整體擴展”這個粒度就夠了但loader和plugin的區(qū)別要能說清。loader是處理文件的轉(zhuǎn)換器plugin是作用于整個構(gòu)建過程的功能擴展。6.3 移動端適配方案A卷末尾偶爾會出一道移動端點屏幕寬度的題目。核心適配方案有三類meta viewport配合remflexible方案以及viewport單位方案。rem方案的核心是動態(tài)設(shè)置根節(jié)點的font-size然后所有尺寸都用rem來寫。假設(shè)設(shè)計稿是750px寬那么把根字體大小設(shè)置為設(shè)備寬度除以7.5在375px寬的設(shè)備上就是50px。這樣設(shè)計稿里的100px換算成rem就是2rem。簡單理解就是屏幕變寬時所有元素等比例放大。viewport方案更直接直接用vw和vh做單位。1vw等于屏幕寬度的百分之一。設(shè)計稿750px那么1px約等于0.1333vw。缺點是需要處理字號太大的場景通常配合min/max做邊界限制。這道題不僅考CSS還考你對“響應(yīng)式”和“自適應(yīng)”這兩個概念的區(qū)分筆試?yán)锎鹌娜瞬簧佟?. 實踐復(fù)盤把真題變成自己的知識地圖7.1 考后怎么復(fù)盤才不白做做完一套題復(fù)盤比刷題更重要。我給自己定過一個標(biāo)準(zhǔn)流程先把錯題按知識點歸類找出重復(fù)出錯的知識點比如“this指向”“Promise時序”“CSS居中”這些高頻點再去刻意練習(xí)同一知識點的不同變形題。復(fù)盤時最好寫“錯題原因”而不是“正確答案”。寫下是因為概念不熟還是因為看漏了條件還是因為邊界沒處理。這三類原因?qū)?yīng)的復(fù)習(xí)策略完全不同。概念不熟就回頭看文檔和書籍看漏條件就訓(xùn)練讀題習(xí)慣邊界沒處理就去多寫不同輸入值的測試用例。另外一個建議是把每道題的代碼在真實環(huán)境里跑一遍不要只用眼睛看輸出。事件循環(huán)和this指向這類題目直接在瀏覽器控制臺里實際執(zhí)行所見即所得比背文章有用得多。跑完之后再試著改改條件比如把普通函數(shù)換成箭頭函數(shù)把setTimeout的延遲時間改成0看看輸出會不會變。這樣一題變多題一套卷子能吃透的知識量是原來的三倍。7.2 筆試答題時間分配的經(jīng)驗線上線下筆試一般給兩個小時左右。以A卷的題量和結(jié)構(gòu)來說我的建議是前30分鐘把比較簡單的基礎(chǔ)題和輸出題一次性寫完中間40分鐘處理重頭的手寫題剩下30分鐘做算法和綜合題最后20分鐘檢查。檢查的重點不是看答案對不對而是看有沒有漏條件。常見的情況是排序題沒有考慮空數(shù)組數(shù)組去重題沒有考慮NaN防抖題沒有傳this。這些邊界條件不會在示例輸入里暴露但判題系統(tǒng)有時候就會卡在這些奇怪的地方。7.3 由一道題延伸到面試問答筆試結(jié)束后通常會有一個拿著你的筆試卷子追問的環(huán)節(jié)。這時候如果你只寫了一個正確答案而沒有想過背后的原理會顯得準(zhǔn)備不足。比如你寫出了數(shù)組去重的Set寫法面試官大概率會問“Set去重和Map去重有什么差別”“能不能不用Set寫”如果只背答案這兩個問題會當(dāng)場卡住。我的習(xí)慣是每做一道筆試題都順手準(zhǔn)備兩個引申問題。比如完成深拷貝后能回答“如何處理循環(huán)引用Set和WeakMap有什么差別為什么用WeakMap”完成防抖后能回答“防抖和節(jié)流的本質(zhì)區(qū)別是什么哪些場景用防抖哪些場景用節(jié)流”這樣筆試不只是筆試它變成了一輪模擬面試。我見過不少同學(xué)把題庫刷了三遍但成績始終徘徊在及格線。問題往往不在于題量而在于“只做題不復(fù)盤”。把每一道題當(dāng)成一個知識錨點連成一張網(wǎng)比刷一百道題再忘掉九十九道要有效得多。最后再分享一個小習(xí)慣每次模擬筆試之后我會把當(dāng)時的錯誤代碼單獨存一個文件持續(xù)積累。過兩周再回來重新跑一遍看自己能不能自行修復(fù)。用這種方式來檢驗知識掌握程度比反復(fù)翻看正確答案更能暴露問題。這份歡聚時代2018校招A卷我的建議不是做完就扔而是留檔作為階段性自測的標(biāo)尺每隔一段時間回頭重做一次你會看到自己的進步曲線。