
1. JavaScript 核心知識體系與面試準備指南作為一名經(jīng)歷過數(shù)十場技術(shù)面試的前端工程師我深知JavaScript基礎(chǔ)知識在面試中的重要性。很多看似簡單的概念在實際工作中卻經(jīng)常成為性能瓶頸和bug源頭。本文將系統(tǒng)梳理JavaScript的核心知識體系幫助開發(fā)者建立完整的知識框架同時針對面試場景提供深度解析。JavaScript作為一門靈活多變的語言其核心機制往往隱藏在簡單的語法背后。理解這些機制不僅能讓你在面試中游刃有余更能提升日常開發(fā)中的問題解決能力。我們將從基礎(chǔ)數(shù)據(jù)類型開始逐步深入到異步編程等高級主題每個部分都會結(jié)合實際面試題進行剖析。2. 基礎(chǔ)數(shù)據(jù)類型與類型判斷2.1 JavaScript的71種數(shù)據(jù)類型JavaScript中的數(shù)據(jù)類型可以分為兩大類原始類型和對象類型。具體包括原始類型Undefined、Null、Boolean、Number、BigInt、String、Symbol對象類型Object包括Array、Function等特殊對象注意typeof null會返回object這是JavaScript早期實現(xiàn)的一個著名bug由于兼容性原因一直保留至今2.2 類型判斷的四種方法typeof操作符typeof 42 // number typeof hello // string typeof undefined // undefined typeof true // boolean typeof Symbol() // symbol typeof {} // object typeof [] // object (注意數(shù)組也是object) typeof function(){} // functioninstanceof操作符 用于檢測構(gòu)造函數(shù)的prototype屬性是否出現(xiàn)在對象的原型鏈上[] instanceof Array // true new Date() instanceof Date // trueObject.prototype.toString 最可靠的類型判斷方法Object.prototype.toString.call([]) // [object Array] Object.prototype.toString.call(null) // [object Null]Array.isArray() 專門用于判斷數(shù)組類型Array.isArray([]) // true Array.isArray({}) // false2.3 類型轉(zhuǎn)換的陷阱面試中經(jīng)??疾旌偷膮^(qū)別1 1 // true (類型轉(zhuǎn)換后比較) 1 1 // false (嚴格比較不轉(zhuǎn)換類型) 0 false // true 0 false // false null undefined // true null undefined // false3. 變量、作用域與閉包3.1 var、let和const的區(qū)別特性varletconst作用域函數(shù)作用域塊級作用域塊級作用域變量提升是否否重復聲明允許不允許不允許初始值可不設(shè)可不設(shè)必須設(shè)置重新賦值允許允許不允許3.2 作用域鏈與閉包閉包是指有權(quán)訪問另一個函數(shù)作用域中的變量的函數(shù)。理解閉包需要掌握詞法作用域函數(shù)在定義時就確定了作用域而非執(zhí)行時執(zhí)行上下文包含變量對象、作用域鏈和this值垃圾回收閉包會阻止被引用的變量被回收經(jīng)典面試題for(var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); } // 輸出五個5如何修改使其輸出0-4解決方案// 使用let for(let i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); } // 或使用IIFE for(var i 0; i 5; i) { (function(j) { setTimeout(function() { console.log(j); }, 1000); })(i); }4. 數(shù)組操作與性能考量4.1 數(shù)組方法分類變異方法會改變原數(shù)組push/pop/shift/unshiftsplice/sort/reversefill/copyWithin非變異方法返回新數(shù)組slice/concatmap/filter/reduceflat/flatMap4.2 數(shù)組遍歷性能對比方法速度可中斷適用場景for循環(huán)最快是需要高性能的場景forEach中等否簡單遍歷for...of慢是需要可讀性的場景map/filter慢否需要返回新數(shù)組的場景4.3 數(shù)組去重的幾種方式// 使用Set const unique arr [...new Set(arr)]; // 使用filter const unique arr arr.filter((item, index) arr.indexOf(item) index); // 使用reduce const unique arr arr.reduce((acc, cur) acc.includes(cur) ? acc : [...acc, cur], []);5. 函數(shù)進階與this指向5.1 箭頭函數(shù)與普通函數(shù)區(qū)別特性普通函數(shù)箭頭函數(shù)this綁定動態(tài)綁定詞法綁定arguments有無構(gòu)造函數(shù)可以不可以prototype有無yield可用不可用5.2 this指向的四種規(guī)則默認綁定非嚴格模式下指向window嚴格模式為undefined隱式綁定作為對象方法調(diào)用時指向該對象顯式綁定通過call/apply/bind指定thisnew綁定構(gòu)造函數(shù)中的this指向新創(chuàng)建的對象5.3 手寫call/apply/bind// call實現(xiàn) Function.prototype.myCall function(context, ...args) { context context || window; const fn Symbol(); context[fn] this; const result context[fn](...args); delete context[fn]; return result; }; // bind實現(xiàn) Function.prototype.myBind function(context, ...args) { const self this; return function(...innerArgs) { return self.apply(context, args.concat(innerArgs)); }; };6. 對象與原型系統(tǒng)6.1 原型鏈示意圖實例對象.__proto__ → 構(gòu)造函數(shù).prototype → Object.prototype → null6.2 繼承的幾種方式原型鏈繼承function Parent() {} function Child() {} Child.prototype new Parent();構(gòu)造函數(shù)繼承function Child() { Parent.call(this); }組合繼承最常用function Child() { Parent.call(this); } Child.prototype Object.create(Parent.prototype); Child.prototype.constructor Child;ES6 class繼承class Child extends Parent { constructor() { super(); } }6.3 深拷貝的實現(xiàn)function deepClone(obj, map new WeakMap()) { if (obj null || typeof obj ! object) return obj; if (map.has(obj)) return map.get(obj); const clone Array.isArray(obj) ? [] : {}; map.set(obj, clone); for (const key in obj) { if (obj.hasOwnProperty(key)) { clone[key] deepClone(obj[key], map); } } return clone; }7. 異步編程模型7.1 事件循環(huán)機制JavaScript的事件循環(huán)執(zhí)行順序執(zhí)行同步代碼執(zhí)行所有微任務(wù)Promise.then, process.nextTick執(zhí)行一個宏任務(wù)setTimeout, setInterval, I/O重復2-3步驟7.2 Promise核心實現(xiàn)class MyPromise { constructor(executor) { this.state pending; this.value undefined; this.reason undefined; this.onFulfilledCallbacks []; this.onRejectedCallbacks []; const resolve value { if (this.state pending) { this.state fulfilled; this.value value; this.onFulfilledCallbacks.forEach(fn fn()); } }; const reject reason { if (this.state pending) { this.state rejected; this.reason reason; this.onRejectedCallbacks.forEach(fn fn()); } }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { return new MyPromise((resolve, reject) { const handleFulfilled () { try { const x onFulfilled(this.value); x instanceof MyPromise ? x.then(resolve, reject) : resolve(x); } catch (err) { reject(err); } }; const handleRejected () { try { const x onRejected(this.reason); x instanceof MyPromise ? x.then(resolve, reject) : resolve(x); } catch (err) { reject(err); } }; if (this.state fulfilled) { handleFulfilled(); } else if (this.state rejected) { handleRejected(); } else { this.onFulfilledCallbacks.push(handleFulfilled); this.onRejectedCallbacks.push(handleRejected); } }); } }7.3 async/await原理async函數(shù)本質(zhì)上是Generator函數(shù)的語法糖其執(zhí)行過程遇到await時會暫停async函數(shù)的執(zhí)行等待Promise解決后繼續(xù)執(zhí)行async函數(shù)如果Promise被拒絕會拋出異常// async/await轉(zhuǎn)換為Promise形式 async function example() { const result await somePromise(); return result 1; } // 等價于 function example() { return somePromise().then(result { return result 1; }); }8. 面試實戰(zhàn)技巧與高頻問題8.1 高頻面試問題整理閉包應(yīng)用場景模塊模式函數(shù)柯里化記憶化函數(shù)事件處理回調(diào)原型鏈相關(guān)問題如何實現(xiàn)繼承instanceof原理是什么new操作符做了什么異步編程問題事件循環(huán)執(zhí)行順序Promise.all/Promise.race實現(xiàn)如何取消Promise8.2 代碼輸出題解析console.log(1); setTimeout(() { console.log(2); Promise.resolve().then(() console.log(3)); }, 0); new Promise((resolve) { console.log(4); resolve(); }).then(() { console.log(5); setTimeout(() console.log(6), 0); }); console.log(7); // 輸出順序1, 4, 7, 5, 2, 3, 68.3 手寫代碼準備清單實現(xiàn)Promise及相關(guān)靜態(tài)方法實現(xiàn)call/apply/bind實現(xiàn)深拷貝實現(xiàn)防抖節(jié)流實現(xiàn)觀察者模式實現(xiàn)數(shù)組扁平化實現(xiàn)函數(shù)柯里化在實際面試中理解概念背后的原理比死記硬背更重要。建議對每個知識點都嘗試自己實現(xiàn)一遍遇到問題時多思考為什么這樣設(shè)計。JavaScript的很多特性都有其歷史原因和實際考量理解這些背景能讓你在面試中給出更有深度的回答。