首页
/ 前端面试全流程复盘:基于 Front End Interview Handbook 的 JavaScript、React 与算法各轮次深度拆解

前端面试全流程复盘:基于 Front End Interview Handbook 的 JavaScript、React 与算法各轮次深度拆解

2026-09-05 11:16:25作者:昌雅子Ethen

本文以 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 的常见陷阱

  • 使用 varfor 循环中配合 setTimeout 时的经典 3, 3, 3 输出——候选人被期望能立刻识别这个闭包陷阱并解释修复方法;
  • 在传给 mapforEach 的回调里使用 this 时,忘记它不会指向外部上下文;
  • 声称类不会被提升——实际上类会提升,但在声明被求值前处于暂时性死区(temporal dead zone)。

最高频题:手写 debouncethrottle

作者反复被问到最多的问题,是手写 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 的用法上:debounceduration 内每次调用都会 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 对比,可以清晰地答出三个差异点:

  1. 并发模型Promise.all 同时发起所有请求,总耗时约等于最慢的单个请求;sequential 逐个发起,总耗时是所有请求耗时之和(示例中 3 个请求各需 1000ms,串行版总耗时 3 秒);
  2. 适用场景:串行执行适用于有副作用、需保序、或后端限流/配额受限的场景(如按顺序翻页、逐条写入);
  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.tslangnostic.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 指南 恰好为这类题目提供了标准作战流程:

  1. 摸清环境:在线还是本地 IDE、能否执行代码预览 UI、是否允许使用框架或必须 vanilla JavaScript、可执行代码并预览;
  2. 澄清问题:能否用最新 JS 语法、浏览器支持范围(影响可用 API);
  3. 管理复杂度:把问题拆成递进的里程碑(基础 UI → 核心交互 → 边缘情况 → 打磨),并把拆解方案告知面试官——UI 编码面试的重点通常是组件状态与 API 设计,而非复杂数据结构与算法;
  4. 边写边测:每完成一个里程碑就在浏览器里验证,而不是全部写完再测;
  5. 收尾三件套:通读代码找基本错误、跑测试用例与边缘情况、说明你做的取舍与未处理的场景。

该指南还特别指出:组件题的评分重心在 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.mdJavaScript 题库CSS 题库HTML 题库
JavaScript 编码 website/contents/javascript-utility-function.mdJavaScript 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 指南的里程碑流程演练一次自动补全组件,最后以树遍历为主的算法题收尾。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
504
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384