前端面试全流程复盘:基于 Front End Interview Handbook 的 JavaScript、React 与算法各轮次深度拆解
本文以 Front End Interview Handbook 仓库中的博客文章 A Glimpse into Front End Interviews 为主体,还原一位一线前端工程师视角下的真实面试流程:每家公司通常 3~4 轮,绝大多数轮次考 JavaScript 与 Web 开发技术讨论,其余为算法或行为面。读完本文,你将掌握各轮次的高频考点——debounce/throttle 手写实现、顺序执行版 Promise.all、Webpack 核心概念、React useEffect 依赖数组等,并能对照仓库中的配套文档制定复习计划。
面试流程总览:3~4 轮,JavaScript 是绝对主角
原文作者结合自身面试经历指出:申请前端工程师职位的流程与申请普通软件工程师职位非常相似,但面试内容差异明显。他的经验是,每家公司通常有 3 到 4 个面试 session,其中大部分考察 JavaScript 与 Web 开发技术讨论,剩余部分考察算法或行为面试。
一个值得注意的现象是:公司越年轻,问题越偏向 JavaScript。作者推测这与"前端工程师"这一岗位本身较新有关——老牌公司过去只招软件工程师,不区分前端还是后端方向。
这一结论与仓库主文档中的公司面试形式调研表相互印证。在 前端面试形式总览 中,仓库汇总了各公司技术轮次的题型分布(Quiz / 算法 / JavaScript 编码 / UI 编码 / 系统设计),例如 Airbnb、Amazon、Google、Microsoft 几乎覆盖全部题型,而 Atlassian、Lyft 则完全不考算法题。该文档同时给出了五大面试格式的完整说明:
- JavaScript coding:Lodash 风格工具函数(
throttle)或语言/DOM API 的 polyfill(Array.prototype.filter()、Promise.all()); - User interface coding:用 HTML/CSS/JavaScript 构建组件、应用或小游戏,其中 Autocomplete(自动补全)被明确标注为"very popular";
- Algorithmic coding:LeetCode 风格算法题,但前端岗位难度通常更低;
- Quiz/trivia:答案明确、可被非技术人员验证的短答题;
- System design:前端系统设计,例如设计聊天应用中的 Emoji 自动补全功能。
仓库根目录的 README 也重申了这一定位:与典型的软件工程师面试相比,前端面试对算法的强调更少,而对 HTML、CSS、JavaScript 等领域知识的考察更细致。
JavaScript 轮:从语言细节到异步编程
JavaScript 语言细节(Minutiae)
JavaScript 是所有受访公司共同的核心考点——如今的前端工作非常"JavaScript 密集",而 HTML/CSS 知识由于组件库的普及已不再是硬性要求。
要进入部分公司,你可能需要复习一些 JavaScript 的"边角细节"。作者列出的实际出现过的话题包括:
- 变量提升(variable hoisting)
- 有空洞的数组(holey/sparse arrays)
- 非严格模式(non-strict mode)
switch的 case 穿透(fall through)
虽然作者本人并不认为懂得这些细节就能决定谁是更好的工程师,但这确实是面试现实。这类问题在仓库中对应 Quiz/trivia 文档 所覆盖的知识面——例如"什么是闭包""Promise 和回调的区别""解释 this 关键字"等;更完整的题目清单见仓库的 JavaScript 题库、HTML 题库 与 CSS 题库。
进阶 JavaScript 话题
通过第一轮笔试后,现场面试(live interview)会转向更进阶的 JavaScript 概念:
- 事件循环(event loop)
- Promise
- async/await
- 作用域与闭包(scope and closures)
如果你已经写过一段时间的 JavaScript 应用并遇到过各种真实场景,这部分不会太难。
仓库的 JavaScript Coding 指南 给出了这些考点的系统化清单,其"Important concepts"表格将核心话题归纳为:
| 类别 | 重点话题 |
|---|---|
| 数据结构 | Arrays, Maps, Stacks, Trees, Sets |
| 算法 | 二分查找、BFS、DFS、递归 |
| JavaScript 语言 | 数据类型与类型转换、作用域、闭包、回调、this 的运作方式、原型与类、箭头函数与普通函数的区别、apply()/call()、Promise、变长参数 |
| DOM | DOM 遍历、创建、操作、访问元素/节点属性、事件委托 |
| 运行时 API | 计时器(setTimeout()、setInterval()) |
该文档还特别指出闭包与 this 的常见陷阱:
- 使用
var在for循环中配合setTimeout时的经典3, 3, 3输出——候选人被期望能立刻识别这个闭包陷阱并解释修复方法; - 在传给
map或forEach的回调里使用this时,忘记它不会指向外部上下文; - 声称类不会被提升——实际上类会提升,但在声明被求值前处于暂时性死区(temporal dead zone)。
最高频题:手写 debounce 与 throttle
作者反复被问到最多的问题,是手写 debounce(防抖)和 throttle(节流)。以下是原文给出的完整实现,可直接复制运行:
function debounce(fn, duration) {
let id;
return function (...args) {
if (id) {
// reset timeout and prevent it from triggering
// if debounced function is called within duration
clearTimeout(id);
}
id = setTimeout(() => {
fn(...args);
}, duration);
};
}
function throttle(fn, duration) {
let id;
return function (...args) {
if (id) {
// if throttled function is called within duration,
// do nothing
return;
}
fn(...args);
id = setTimeout(() => {
id = null; // release "lock"
}, duration);
};
}
// usage example
const helloWorld = () => {
console.log('hello world');
};
const debouncedHelloWorld = debounce(helloWorld, 1000);
const throttledHelloWorld = throttle(helloWorld, 1000);
(代码来源见 博客原文。)
两者的核心差异在闭包变量 id 的用法上:debounce 在 duration 内每次调用都会 clearTimeout 重置计时器,只有静默 duration 毫秒后回调才真正执行,适合"用户停止输入后再触发"的场景(如搜索框、窗口 resize);throttle 则用一个"锁"保证 duration 内最多执行一次,首次调用立即执行,适合限流场景(如滚动监听、鼠标移动)。
结合仓库 JavaScript Coding 指南 中总结的面试常见失分点,上面的基础实现还可以主动补充以下细节(即使不实现,口头提及也能加分):
- 用箭头函数写计时器回调时可能丢失
this——暗示不了解普通函数与箭头函数的绑定差异; - 忘记在后续调用时清除上一个计时器,实际上就把
debounce写成了throttle; - 忘记用 rest/spread 转发参数;
- 没有主动澄清
leading(前边缘)还是trailing(后边缘)行为——这是面试官常追问的点,上述实现均为 trailing 边缘(throttle为 leading 边缘)。
指南同时提醒:JavaScript 工具函数页 明确指出 Lodash 的官方实现"过度工程化"——大量复用抽象函数并兼容老旧浏览器的怪异场景,面试中并不期望你覆盖这些边缘用例;基础题的期望耗时约为 10~15 分钟,若确认拿到的是基础题,应尽量在此时间内完成,因为通常后面还有第二题。
次高频题:手写顺序执行的 Promise.all
作者被问到第二多的问题是实现一个"顺序版的 Promise.all"。原文代码(TypeScript 标注,语法即标准 JavaScript)如下:
function sequential(data, fetcher) {
const helper = (index, results) => {
if (index === data.length) {
return results;
}
return fetcher(data[index]).then((datum) => {
results.push(datum);
return helper(index + 1, results);
});
};
return helper(0, []);
}
// usage example
const fetcher = (i) => {
return new Promise((resolve) => {
setTimeout(() => resolve(i), 1000);
});
};
sequential([1, 2, 3], fetcher);
(代码来源见 博客原文。)
这段代码的精髓在于用递归 + .then 链实现严格串行:helper(0, []) 从索引 0 开始,只有 fetcher(data[index]) resolve 后才发起下一个请求,结果按输入顺序压入 results。与原生 Promise.all 对比,可以清晰地答出三个差异点:
- 并发模型:
Promise.all同时发起所有请求,总耗时约等于最慢的单个请求;sequential逐个发起,总耗时是所有请求耗时之和(示例中 3 个请求各需 1000ms,串行版总耗时 3 秒); - 适用场景:串行执行适用于有副作用、需保序、或后端限流/配额受限的场景(如按顺序翻页、逐条写入);
- 结果顺序与失败语义:
Promise.all保证结果与输入顺序一致(而非 resolve 顺序),且是 fail-fast——任一 reject 即整体 reject。
JavaScript Coding 指南 中 Promise.all 相关的高频错误恰好可以作为这道题的应答要点:结果数组若按 resolve 顺序 push 会导致输出顺序不确定;空输入必须直接 resolve 为 [] 而不是让 Promise 永久 pending;要分清 Promise.all(fail-fast)与 Promise.allSettled(永不 reject);并且应该用 Promise.resolve(value) 归一化非 Promise 输入。指南还建议:即使跳过了某些边缘用例,也要主动喊出这些规格细节——"知道世界上存在什么"本身就是资深信号。
讨论轮:工具生态与框架深度
Web 开发工具
"无论我们多么不愿承认,Web 开发工具已经是一个日益复杂多样的生态。"小公司尤其是创业公司,要求工程师真正理解这些工具;大公司则能把工具链复杂度从工程师面前抽象掉(除非岗位明确要求)。因此 Webpack 与 Babel 成为常见讨论话题。
原文给出的检验标准是:对 Webpack 的良好理解,意味着你能解释清楚以下概念:
- 什么是打包(bundling)
- 什么是 tree-shaking(摇树优化)
- 什么是懒加载(lazy-loading),以及它为什么重要
- loader 是如何工作的
这些概念在仓库的工程侧同样真实存在:本仓库网站部分基于 Docusaurus 构建,其入口配置 docusaurus.config.js 就是打包链路的产物载体(标题为 "The Official Front End Interview Handbook 2026"),而根目录的 vite.config.ts 与 langnostic.config.ts 则展示了 Vite 与文档翻译工具链的配置形态——从源码结构看,这正是"工具生态"的典型切面:不同的 bundler/loader 体系服务于不同的产物目标。
React 或你选择的框架
如果岗位要求 React,你可能会被要求回答甚至现场编写 React 组件。即使没有 React 经验,使用其他框架也是可行的,前提是你能够清晰解释正在发生的事情。
考察范围从"现场实现一个功能"到"回答/解释一些 React 概念"不等,原文点名的例子包括 useEffect 的依赖数组和 shouldComponentUpdate。
仓库内的 React Interview Playbook 对这些考点有完整展开,例如:
useEffect的签名是useEffect(effectFunction, dependencyArray),它本质上是"将组件与外部系统同步"的钩子——执行副作用、订阅事件、操作 DOM;useState更新依赖前值时应使用函数式写法setCount((prevCount) => prevCount + 1),因为在 interval、异步回调和快速连续更新中,闭包捕获的count是陈旧值;- 直接原地修改 state 对象(
user.age = 26)不会触发重渲染,因为 React 按引用比较来决定是否重渲染。
Playbook 还包含 React 表单、数据获取、设计模式 与 注册表单实战示例,覆盖了"现场实现一个功能"这类提问所需的全部素材。
工作經歷深挖
除 JavaScript 与框架两大主题外,面试官还会从你的简历中挑一两件"看起来有意思"的事让你展开。作者以自己编写 Babel 插件和 jscodeshift code mod 的经历为例,向面试官完整讲解了自己如何利用这些工具改进公司代码库——这正是"把工具链理解讲出业务价值"的示范。
实现轮:一次 Google 式自动补全搜索框
作者在所有面试中只被要求实现过一次功能(共两次),这并不常见,但确实会出现。这轮考察的是你对 HTML/CSS 基础、工具与框架的熟悉程度。他遇到的题目是:实现一个类似 Google 的自动补全搜索框——如果你以前构建过类似的东西,一小时内可以完成。
实现轮与算法轮的感受非常相似:你需要在边思考、边说出决策理由的同时,主动寻找最优解。
仓库的 User Interface Coding 指南 恰好为这类题目提供了标准作战流程:
- 摸清环境:在线还是本地 IDE、能否执行代码预览 UI、是否允许使用框架或必须 vanilla JavaScript、可执行代码并预览;
- 澄清问题:能否用最新 JS 语法、浏览器支持范围(影响可用 API);
- 管理复杂度:把问题拆成递进的里程碑(基础 UI → 核心交互 → 边缘情况 → 打磨),并把拆解方案告知面试官——UI 编码面试的重点通常是组件状态与 API 设计,而非复杂数据结构与算法;
- 边写边测:每完成一个里程碑就在浏览器里验证,而不是全部写完再测;
- 收尾三件套:通读代码找基本错误、跑测试用例与边缘情况、说明你做的取舍与未处理的场景。
该指南还特别指出:组件题的评分重心在 API 设计与可访问性(prop 形状、ARIA 角色、键盘支持)而非像素级视觉还原;对于 Autocomplete 这类经典题,指南将 App/游戏类题型的允许时间描述为可达半小时甚至一小时,与博客"一小时内可实现"的经验一致。另外,部分公司(如 Google)强制要求使用 Vanilla JavaScript 而非框架,备考时应确保裸写 DOM 的功力。
算法轮:为什么作者选了 Python
前端工程师终究是软件工程师,具备基本的算法与数据结构能力是预期内的,公司通常不会出很难很偏的算法题。作者在自认算法是弱项的前提下投入了最多的复习时间。
一个颇具反讽的点是:他最终选择了 Python 而不是 JavaScript 来写算法。原因在于 JavaScript 缺乏原生的 min-heap(最小堆)与二分查找实现,使其成为"略差"的选择。
结合仓库的 Algorithms 文档 可以看到补充视角:前端面试中的树题格外重要,因为 DOM 本身就是一棵树——如果只能优先准备一种数据结构,应聚焦树以及 BFS、DFS、层序遍历等常见树遍历算法。文档也证实了"前端面试算法难度整体偏低"的判断:公司在算法题上通常对前端候选人"手下留情"。
结论:更专,也可能更努力
原文的收尾判断是:前端软件工程师岗位与一般软件工程师岗位没有本质不同,但它更专业化,某些方面甚至要求付出更多努力。如果你对这一领域有热情、对所做之事感兴趣,这就不会是一道难以跨越的门槛。
落到本仓库,这篇博客所覆盖的每一轮都有一份可直接执行的配套文档:
| 面试轮次 | 仓库配套资料 |
|---|---|
| 面试形式总览 | website/contents/introduction.md |
| Quiz/Trivia | website/contents/trivia.md、JavaScript 题库、CSS 题库、HTML 题库 |
| JavaScript 编码 | website/contents/javascript-utility-function.md、JavaScript Coding 指南 |
| UI 实现 | User Interface Coding 指南 |
| React 框架 | React Hooks 指南、React 表单 |
| 算法 | website/contents/algorithms.md |
| 系统设计 | Front End System Design |
建议的复习路径是:先以 Quiz 文档扫盲语言细节,再精做 debounce/throttle 与顺序 Promise.all 两道最高频手写题(并主动补充 leading/trailing 边缘、空输入、fail-fast 等追问点),然后用 UI 指南的里程碑流程演练一次自动补全组件,最后以树遍历为主的算法题收尾。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00