微步在线前端开发工程师笔试+面试准备
微步在线前端开发工程师通过牛客进行笔试的备考入口,整理公开回忆题、牛客题型风格、高频考点和准备建议。
[!info] related notes
微步在线前端开发工程师笔试+面试准备
微步在线前端岗的公开“完整原卷”不多,但牛客回忆题已经足够看出风格:React 和浏览器原理占比不低,JS/CSS 基础必须稳,算法不会缺席。
这篇怎么用
- 先看自己能不能口头回答文中的高频题。
- 不会的题,顺着后面的相关笔记补知识点。
- 真到牛客笔试前,只看“复现版题单”和“最后速背清单”。
目前公开信息的稳定判断
比较明确的情况有两点:
- 微步在线前端岗的牛客公开题库很少,公开页更容易看到的是面经和回忆题,而不是整套原卷。
- 从岗位要求和回忆题看,前端岗会稳定覆盖:数据结构与算法、网络基础、HTML/CSS/JavaScript、HTTP、浏览器原理、React。
因此最稳的准备方式不是等完整原卷,而是把公开回忆题吃透,再按 JD 主线把关联知识补齐。
题型画像
从公开回忆看,微步前端笔试/技术面比较像这几类题的组合:
- CSS 布局和样式覆盖
- JS 基础与输出题
- Promise / 事件循环 / 异步错误处理
- React Hooks / JSX / key / Fiber
- 浏览器缓存、关键渲染路径、HTTP
- 一到两道常规算法题
如果按牛客常见形式推断,比较像“选择/简答/代码题混合”,不是纯算法场。
已公开的高价值回忆题
1. 2024 微步前端实习回忆
这一组非常贴近前端笔试:
- 容器垂直居中 → CSS 基线与垂直对齐, Flex 布局
- 文字垂直居中 → CSS 基线与垂直对齐
- 左固定右自适应两栏布局 → CSS 两栏布局
- CSS 选择器权重 → CSS 优先级与特异性
- 组件库样式覆盖和覆盖原则 → 覆盖组件库样式
- JS 基本数据类型 → ECMAScript 原始值与引用值
- 引用类型和基本类型区别 → ECMAScript 原始值与引用值
- 类型判断方式 → ECMAScript 类型检测
- 数组判断 → Array.isArray
var/let/const区别 → var / let / const 区别- 变量提升 → 变量提升
- Promise 理解 → ECMAScript Promise 与异步
- 把普通 ajax 封装成 Promise → Promise 封装回调式异步
- 事件循环、宏任务、微任务 → JS 事件循环, 事件循环面试题
- 给定代码输出顺序 → JS 事件循环, ECMAScript 闭包
- 数组洗牌算法 → Fisher-Yates 洗牌
- React 常用 hooks → React Hooks, React Hooks 总览
- 列表
key的作用与不加key的影响 → React 列表渲染与 key
2. React 原理和工程化向回忆题
这组题更偏一面或深挖:
for + setTimeout输出和闭包修复 → ECMAScript 闭包, var / let / const 区别- 事件循环 → JS 事件循环
- Promise + setTimeout 输出题 → ECMAScript Promise 与异步, JS 事件循环
- 手写 Promise 链式调用 → Promise 链
- JSX 到
React.createElement的转换 → JSX, Babel 与 AST - React Fiber → Fiber MOC
- React 16 之前的 Stack 架构 → React Stack 架构, Stack vs Fiber 对比
- Hooks → React Hooks, React Hooks 面试深挖
- webpack、babel、AST → webpack 构建流程, Babel 与 AST
- 浏览器缓存:
304、memory cache、disk cache→ 浏览器缓存分类与 304 / memory cache / disk cache - 两数之和 → 两数之和
- 关键渲染路径 → 关键渲染路径
3. 前端一面 / 二面回忆
这组更偏面试现场:
- 垂直居中布局 → CSS 基线与垂直对齐, Flex 布局
new做了什么 → new 操作符, ECMAScript 原型this指向和输出 → this 关键字- Promise 状态理解 → ECMAScript Promise 与异步
- Vue 里
data为什么是函数 → Vue MOC - 项目题:购物车小球下落动画、贝塞尔曲线起终点怎么定 → CSS 基线与垂直对齐
key的用途 → React 列表渲染与 key- Promise 异常处理 → 异步错误处理
try/catch能不能抓到点击事件 /setTimeout里的异常 → 异步错误处理, JS 事件循环- 多个
fetch怎么都完成后再继续 → 异步错误处理 - 两个栈实现队列 → 用栈实现队列
高频考点清单
CSS / 布局
- 垂直居中 → CSS 基线与垂直对齐, Flex 布局
- 两栏布局 → CSS 两栏布局
- 选择器权重 → CSS 优先级与特异性
- 组件库样式覆盖 → 覆盖组件库样式
JS / 语言机制
- 基本类型和引用类型 → ECMAScript 原始值与引用值
- 数组和类型判断 → Array.isArray, ECMAScript 类型检测
var/let/const→ var / let / const 区别- 变量提升 → 变量提升
this→ this 关键字- 闭包 → ECMAScript 闭包
- 原型和
new→ ECMAScript 原型, new 操作符
异步 / Promise
- Promise 状态流转 → ECMAScript Promise 与异步
- Promise 链式调用 → Promise 链
- 回调改 Promise → Promise 封装回调式异步
- 事件循环 → JS 事件循环
- 宏任务 / 微任务 → 事件循环面试题
- 异步错误能不能被
try/catch抓到 → 异步错误处理
React
- 常用 Hooks → React Hooks, React Hooks 总览
key→ React 列表渲染与 key- JSX 转换 → JSX
- Fiber → Fiber MOC
- Stack 架构和 Fiber 的差异 → Stack vs Fiber 对比
浏览器 / 网络 / 工程化
- HTTP → HTTP 与前端网络 MOC
- 浏览器缓存 → 浏览器缓存分类与 304 / memory cache / disk cache
- 关键渲染路径 → 关键渲染路径
- 浏览器原理 → 浏览器渲染过程
- webpack / babel / AST → webpack 构建流程, Babel 与 AST
算法
- 数组洗牌 → Fisher-Yates 洗牌
- 两数之和 → 两数之和
- 两个栈实现队列 → 用栈实现队列
- 全排列 → 全排列回溯模板
- 斐波那契 → 斐波那契
- 高亮关键词(DOM 文本节点遍历)→ [[#10 分钟刷题:高亮关键词|高亮关键词]]
- 简易模板渲染引擎({{key}} 插值解析)→ [[#10 分钟刷题:简易模板渲染引擎|简易模板渲染引擎]]
10 分钟刷题:简易模板渲染引擎
这题考字符串替换、嵌套属性访问和容错处理。最稳的写法是
replace配合回调函数,再把{{user.name}}按.逐层取值,取不到就返回空字符串。
题目:实现 renderTemplate(template, data),解析模板中的 {{key}} 并替换为 data 中对应的值。
要求:支持嵌套属性、属性不存在时渲染为空字符串、允许大括号内部有空格。
function renderTemplate(template, data) {
const regex = /\{\{\s*(.+?)\s*\}\}/g;
return template.replace(regex, (match, key) => {
const paths = key.trim().split('.');
let value = data;
for (const path of paths) {
if (value === undefined || value === null) {
return '';
}
value = value[path];
}
return value === undefined || value === null ? '' : String(value);
});
}
10 分钟刷题:高亮关键词
这题考 DOM 遍历和文本节点处理。不要直接替换
innerHTML,而是只处理TextNode,再用正则分割重建节点。
题目:实现 highlight(word),只高亮 jsContent 节点内的文本,不能跨标签。
要求:支持前缀递减匹配、同一内容只高亮一次、不能跨标签高亮、高亮格式用 <em>。
function highlight(word) {
if (!word) return;
const root = document.getElementById('jsContent');
if (!root) return;
const prefixes = [];
for (let i = word.length; i > 0; i--) {
prefixes.push(word.slice(0, i).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
}
const regex = new RegExp(`(${prefixes.join('|')})`, 'g');
function processNode(node) {
const children = Array.from(node.childNodes);
for (const child of children) {
if (child.nodeType === 3) {
const text = child.nodeValue;
if (!text) continue;
const parts = text.split(regex);
if (parts.length <= 1) continue;
for (let i = 0; i < parts.length; i++) {
if (!parts[i]) continue;
if (i % 2 !== 0) {
const em = document.createElement('em');
em.textContent = parts[i];
node.insertBefore(em, child);
} else {
node.insertBefore(document.createTextNode(parts[i]), child);
}
}
node.removeChild(child);
} else if (child.nodeType === 1) {
processNode(child);
}
}
}
processNode(root);
}
牛客复现版题单
下面这组题可以直接按”微步笔试模拟卷”去刷,每题后附相关原子笔记链接,点击即可跳转复习:
- 写出 3 种垂直居中方案,并说明适用场景 → CSS 基线与垂直对齐, Flex 布局, CSS 定位
- 左侧固定、右侧自适应两栏布局怎么写 → CSS 两栏布局, BFC
- CSS 选择器权重怎么比较 → CSS 优先级与特异性, CSS 选择器与伪类伪元素
- 组件库样式为什么有时覆盖不掉,怎么处理 → 覆盖组件库样式, CSS 优先级
- JS 基本类型有哪些,怎么判断数组 → ECMAScript 原始值与引用值, Array.isArray, ECMAScript 类型检测
var/let/const区别是什么,变量提升怎么理解 → var / let / const 区别, 变量提升- Promise 的状态和链式调用怎么实现 → ECMAScript Promise 与异步, Promise 链
- 把一个回调风格的 ajax 封装成 Promise → Promise 封装回调式异步
- 说出事件循环输出顺序,并解释宏任务和微任务 → JS 事件循环, 事件循环面试题
for + setTimeout为什么会那样输出,怎么用闭包修 → ECMAScript 闭包, var / let / const 区别- React 中
key为什么重要,不写会怎样 → React 列表渲染与 key, key 属性面试题 - JSX 为什么会变成
React.createElement→ JSX, Babel 与 AST - Fiber 相比旧架构解决了什么问题 → Fiber MOC, React Stack vs Fiber 架构对比
- 浏览器缓存有哪些,
304/memory cache/disk cache分别是什么 → 浏览器缓存分类与 304 / memory cache / disk cache - 简易模板渲染引擎 → [[#10 分钟刷题:简易模板渲染引擎|简易模板渲染引擎]]
- 高亮关键词 → [[#10 分钟刷题:高亮关键词|高亮关键词]]
- 什么是关键渲染路径 → 关键渲染路径
- 手写数组洗牌 → Fisher-Yates 洗牌
- 手写两数之和 → 两数之和
- 手写两个栈实现队列 → 用栈实现队列
- 手写全排列 → 全排列回溯模板, 回溯算法
- 手写斐波那契 → 斐波那契
复现题参考答案速记
垂直居中
速记答案:
最通用的是 Flex:父容器
display: flex; justify-content: center; align-items: center;。已知宽高时也可以绝对定位加transform: translate(-50%, -50%)。单行文字垂直居中还能用line-height等于容器高度,但场景比较窄。
左固定右自适应两栏布局
速记答案:
最稳的是 Flex,左边固定宽度,右边
flex: 1。也可以左边 float、右边形成 BFC,或者右边用calc(100% - 200px)。面试里优先说 Flex,因为表达最直接。
CSS 选择器权重
速记答案:
行内样式最高,然后是 id,再是 class / 属性 / 伪类,最后是标签和伪元素。比较权重时是按位比较,不是简单十进制相加。
!important会强行提高优先级,但不应该滥用。
组件库样式为什么覆盖不掉
速记答案:
一般先看四件事:选择器优先级够不够、样式加载顺序对不对、组件库有没有内联样式、样式是不是被 CSS Modules 或作用域机制隔离了。更稳的做法是优先走组件库提供的主题、变量和扩展 class,而不是一味堆选择器。
JS 基本类型和数组判断
速记答案:
基本类型有
number、string、boolean、undefined、null、symbol、bigint。数组判断最稳的是Array.isArray()。更通用的类型判断可以用Object.prototype.toString.call(x)。
var / let / const 和变量提升
速记答案:
var存在函数作用域和变量提升,未赋值前访问是undefined;let和const是块级作用域,存在暂时性死区,声明前访问会报错;const不能重新赋值,但如果值是对象,内部属性仍可改。函数声明也会提升,而且通常先于var生效。
Promise 的状态和链式调用
速记答案:
Promise 只有
pending、fulfilled、rejected三种状态,一旦从pending变成后两者之一就不会再变。链式调用本质上是then返回一个新的 Promise,前一个then的返回值会传给下一个。
把 ajax 封装成 Promise
速记答案:
核心就是在 Promise 构造函数里发请求,请求成功时
resolve,失败时reject。重点不是 API 细节,而是把回调式异步改成状态驱动的结果对象。
function ajaxPromise(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.responseText);
} else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = () => reject(new Error('network error'));
xhr.send();
});
}
事件循环
速记答案:
先执行同步代码;当前宏任务执行完后,先清空微任务队列;再进入下一个宏任务。Promise 的
then、catch、finally回调属于微任务,setTimeout、setInterval属于宏任务。
for + setTimeout 为什么输出一样,怎么修
速记答案:
如果用
var,循环结束后所有回调共享同一个变量,所以最后拿到的是同一个值。修法有三种:改成let、用立即执行函数形成闭包、给setTimeout额外传参。
React 的 key
速记答案:
key是列表节点的稳定身份标识。框架 diff 时靠它判断哪些节点是新增、删除、复用。没有key或乱用index,在插入、删除、排序场景下容易出现状态错位和不必要重渲染。
JSX 为什么会变成 React.createElement
速记答案:
JSX 不是浏览器原生语法,它只是语法糖。编译阶段会被 Babel 一类工具转成
React.createElement(...)或新的 JSX Runtime 调用,本质上仍然是在创建 React Element 对象。
Fiber 解决了什么问题
速记答案:
它解决的是旧版 React 更新过程不可中断、长任务容易阻塞主线程的问题。Fiber 把更新拆成更细的工作单元,可以暂停、恢复、按优先级调度,让页面在复杂更新时仍然更容易保持响应。
浏览器缓存里的 304 / memory cache / disk cache
速记答案:
304是协商缓存命中,资源没变,服务器告诉你继续用本地副本。memory cache是内存缓存,速度最快,通常当前页面生命周期里更常见。disk cache是磁盘缓存,持久性更强,但读取比内存慢。
关键渲染路径
速记答案:
浏览器拿到 HTML 后会解析 DOM,解析 CSS 生成 CSSOM,再合成 Render Tree,之后做 Layout 和 Paint。性能优化通常围绕减少阻塞资源、缩短关键路径、降低重排重绘成本来做。
数组洗牌
速记答案:
用 Fisher-Yates 洗牌。核心是从后往前遍历,每次在
[0, i]随机选一个位置交换,这样才能保证等概率。
function shuffle(arr) {
const res = arr.slice();
for (let i = res.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[res[i], res[j]] = [res[j], res[i]];
}
return res;
}
两数之和
速记答案:
用哈希表记录已经遍历过的值和下标。每次先看目标差值在不在表里,在就返回,不在就把当前值存进去。时间复杂度
O(n)。
两个栈实现队列
速记答案:
一个栈负责入队,一个栈负责出队。出队时如果输出栈为空,就把输入栈元素全部倒过去,再弹出栈顶。这样均摊复杂度是
O(1)。
全排列
速记答案:
典型回溯题。维护路径和已使用标记,每层选一个还没用过的数加入路径,递归到底后收集答案,再回溯撤销选择。
斐波那契
速记答案:
最容易超时的是纯递归。面试更稳的是迭代版或 DP,用两个变量滚动保存前两项,时间
O(n),空间O(1)。
这些题至少要答到什么程度
CSS / 布局
- 垂直居中:至少能讲 Flex、绝对定位+transform、行高或 Grid
- 两栏布局:至少能写 Flex、BFC 或 calc 方案
- 样式覆盖:至少知道优先级、选择器层级、样式注入顺序、CSS Modules / scoped 的边界
JS / 异步
- 类型判断:至少会
typeof、instanceof、Array.isArray()、Object.prototype.toString.call - 变量提升:至少知道
var声明提升、let/const暂时性死区、函数声明提升 - 事件循环:至少能准确说清同步、微任务、宏任务顺序
- Promise:至少能讲状态只能从
pending变成fulfilled/rejected,且状态一旦确定不可逆
React
- Hooks:至少能讲
useState、useEffect、useRef的边界 key:至少能讲“稳定标识节点身份,不建议乱用 index”- Fiber:至少知道它是为了让渲染任务可中断、可分片调度,而不是一次性阻塞主线程
浏览器 / 工程化
- 缓存:至少知道强缓存、协商缓存,以及 DevTools 里常见的
memory cache/disk cache - 关键渲染路径:至少能从 HTML 解析、CSSOM、Render Tree、Layout、Paint 主线讲下来
- webpack / babel:至少知道 webpack 是打包构建工具,babel 负责语法转换,AST 是代码的语法树表示
典型问答模板
组件库样式为什么覆盖不掉
推荐答法:
先看是不是优先级不够,再看样式注入顺序,最后看组件库本身有没有内联样式、CSS Modules 或更高优先级规则。覆盖组件库样式不能只想着加更长选择器,还要判断是否会造成维护成本失控。更稳的做法是优先使用它提供的主题、变量或 class 扩展能力,实在不行再做局部覆盖。
Promise 和事件循环为什么总一起问
推荐答法:
因为 Promise 回调会进微任务队列,很多输出题本质上考的是你知不知道同步代码先执行,当前宏任务结束后清空微任务,再执行下一个宏任务。只会背 Promise API 但讲不清事件循环,通常一做输出题就会乱。
Fiber 相比旧 Stack 架构解决了什么
推荐答法:
旧的 Stack Reconciler 更像一次性递归执行,任务一旦开始就不容易打断,复杂页面更新时可能长时间占用主线程。Fiber 的关键改进是把更新过程拆成更细的小任务,让调度更灵活,可以中断、恢复和分优先级处理,从而改善大更新场景下的响应性。
为什么 try/catch 抓不到某些异步错误
推荐答法:
因为
try/catch只能捕获当前同步调用栈里的异常。像setTimeout回调、点击事件回调这类异步任务,执行时已经不在原来的调用栈里了,所以外层try/catch抓不到。Promise 场景一般要配合.catch()或async/await + try/catch在对应异步边界里处理。
手写题优先级
第一优先
- 两数之和
- 两个栈实现队列
- 数组洗牌
第二优先
- 全排列
- 斐波那契
- Promise 封装题
第三优先
- 手写 Promise 链式调用
这一题更偏一面深挖,不一定是笔试必出,但很能区分你对异步模型到底理解到哪一层。
对应知识点链接
CSS
- 垂直居中 -> CSS 基线与垂直对齐, Flex 布局, CSS 定位
- 两栏布局 -> CSS 两栏布局, Flex 布局, BFC
- 选择器权重 -> CSS 优先级与特异性, CSS 选择器与伪类伪元素
- 组件库样式覆盖 -> 覆盖组件库样式, CSS 优先级
- Flex vs Grid -> Flex 和 Grid 的区别
- 定位 -> CSS position
- BFC -> BFC 是什么,有什么作用
JS 基础
- 基本类型 / 引用类型 -> ECMAScript 原始值与引用值, ECMAScript 基本引用类型
- 类型判断 -> ECMAScript 类型检测, typeof, instanceof, Array.isArray, Object.prototype.toString
- var / let / const -> var / let / const 区别
- 变量提升 -> 变量提升
- this -> this 关键字, this 与显式绑定
- 闭包 -> ECMAScript 闭包, 闭包面试题
- 原型链 -> ECMAScript 原型, 原型链面试题, proto / prototype / constructor
- new -> new 操作符
异步 / Promise
- Promise 状态 -> ECMAScript Promise 与异步, Promise 链
- Promise 链式调用 -> Promise 链
- Promise 封装 ajax -> Promise 封装回调式异步
- 事件循环 -> JS 事件循环, 浏览器与 Node 事件循环对比, 事件循环面试题
- 宏任务 / 微任务 -> JS 事件循环, 事件循环面试题
- 异步错误处理 -> 异步错误处理
React
- Hooks -> React Hooks, React Hooks 总览, React Hooks 面试深挖, React 自定义 Hooks
- key -> React 列表渲染与 key, key 属性面试题
- JSX -> JSX
- Fiber -> Fiber MOC
- Stack 架构 -> React Stack 架构
浏览器 / 网络 / 工程化
- HTTP 缓存 -> 浏览器缓存分类与 304 / memory cache / disk cache
- 关键渲染路径 -> 关键渲染路径
- URL 到渲染 -> URL 到页面渲染, 输入 URL 后发生了什么, 浏览器渲染过程
- webpack 构建 -> webpack 构建流程, webpack 打包流程面试题
- babel / AST -> Babel 与 AST
- loader / plugin -> webpack loader 和 plugin, Loader vs Plugin
- HTTP/1.1 vs HTTP/2 -> HTTP1.1 vs HTTP2
算法
备考顺序建议
- 先刷 JS 基础:类型判断、提升、
this、new、闭包 - 再刷异步:Promise、事件循环、输出题、异步异常
- 再刷 React:Hooks、
key、JSX、Fiber - 再刷浏览器和工程化:缓存、渲染路径、webpack/babel/AST
- 最后补算法:两数之和、队列栈、全排列、斐波那契
最后速背清单
- CSS:垂直居中、两栏布局、选择器权重、样式覆盖
- JS:类型判断、提升、
this、闭包、new - 异步:Promise、事件循环、宏任务微任务、异步错误处理
- React:Hooks、
key、JSX、Fiber - 浏览器:缓存、关键渲染路径、HTTP
- 算法:两数之和、数组洗牌、两个栈实现队列、全排列、斐波那契
实战提醒
- 微步前端不是只问“会不会 React API”,而是会顺着 React 继续问到调度、渲染和工程链路。
- 牛客题库公开量不高,所以你要重点吃透“回忆题 + JD 高频点”的交集,而不是等完整原题。
- 如果时间不够,优先保证 JS 异步、React、浏览器缓存和两三道基础算法题能稳定答出来。
一面模拟问答
这一段原来是独立的一面模拟问答,现在合并到主笔记里,方便统一维护。
自我介绍
我是 XX,主要做前端开发,平时接触比较多的是 JavaScript、React 或 Vue,以及常见的前端工程化实践。最近我在重点补浏览器、网络、异步模型和 React 原理,因为我希望找的是既要求基础扎实,也要求工程落地能力的前端岗位。做项目时我比较关注需求拆解、问题定位和交付质量,所以这次也主要在投对前端基础和工程能力要求更高的团队。
介绍一个你最满意的项目
我最满意的项目是 X。这个项目的目标是 Y,我主要负责 A、B 两个模块。这里面最有挑战的问题是 Z,因为它同时影响用户体验和研发效率。我当时先确认问题边界,再比较不同方案的成本和收益,最终选了一个更稳的方案落地。结果上,这个项目把某个关键指标优化到了 N,或者把某类线上问题明显压下来了。
这个项目里最难的问题是什么
最难的不一定是代码量最大,而是那种会同时影响稳定性、体验和协作效率的问题。对我来说最难的是 X,因为它不是单点 bug,而是涉及到 A、B、C 几个环节。我当时先把问题拆开,定位是前端状态管理、接口时序还是服务端返回问题,再逐步收敛方案。
你具体做了什么
我主要负责需求拆解、核心页面实现、接口联调,以及上线后的问题排查和小范围性能优化。真正由我主导的部分是 A 和 B,因为这两个模块既直接影响用户主流程,也更容易暴露稳定性问题。
如果现在让你重做一次,你会怎么优化
如果重做一次,我会优先优化两类东西。第一类是结构性问题,比如状态边界、组件拆分、接口层抽象;第二类是观测能力,比如埋点、日志、错误监控。这样后面无论是定位问题还是继续迭代,成本都会更低。
var、let、const 的区别
关键区别在作用域和提升表现。
var是函数作用域,会提升,声明前访问得到undefined;let和const是块级作用域,存在暂时性死区,声明前访问会报错;const不能重新赋值,但如果是对象,内部属性仍可改。
this 是怎么确定的
this不是定义时决定,而是调用位置决定。普通函数默认绑定看运行环境;对象方法调用时指向调用它的对象;call/apply/bind可以显式指定;箭头函数没有自己的this,会捕获外层词法作用域的this。
事件循环怎么理解
先执行同步代码,当前宏任务结束后清空微任务队列,再进入下一个宏任务。Promise 的回调是微任务,
setTimeout是宏任务。很多输出题本质上考的就是你能不能分清这条执行顺序。
为什么 try/catch 抓不到 setTimeout 里的错误
因为外层
try/catch只能捕获当前同步调用栈里的异常。setTimeout回调真正执行时已经是后面的宏任务了,不在原来的调用栈里,所以抓不到。异步场景要在对应异步边界里处理错误,比如 Promise 的.catch()或async/await + try/catch。
Promise 为什么适合链式调用
因为
then会返回一个新的 Promise,前一个步骤的结果可以自然传给下一个步骤。这样可以把原本层层嵌套的回调改成线性的异步流程,代码更容易维护,也更容易统一处理错误。
React 中 key 有什么用
key是列表节点的稳定身份标识。React diff 时会用它判断哪些节点需要复用、哪些节点是新增或删除。如果没有key或乱用index,在插入、删除、排序场景下容易出现状态错位和额外渲染。
常用 Hooks 你怎么理解
useState用来管理组件内部状态,useEffect用来处理副作用,比如订阅、请求、同步外部系统,useRef用来保存不触发重渲染的可变值,或者拿 DOM 引用。面试里最好别只背 API,而是讲清它们各自的边界。
JSX 为什么能运行
JSX 本身不是浏览器认识的语法,它会先被编译工具转换成
React.createElement(...)或新的 JSX Runtime 调用,最终本质上还是在创建 React Element 对象。
Fiber 相比旧架构解决了什么问题
旧版更新过程更像一次性递归,任务开始后不容易打断,复杂更新时容易长时间占用主线程。Fiber 把任务拆细后,更新可以暂停、恢复、按优先级调度,所以在复杂场景下页面更容易保持响应。
浏览器缓存怎么答
先区分强缓存和协商缓存,细节可看 浏览器缓存分类与 304 / memory cache / disk cache。强缓存命中时直接用本地副本,不发请求;协商缓存会发请求让服务端确认资源有没有变化,常见结果是
304。DevTools 里还经常能看到memory cache和disk cache,它们更多是在说明缓存副本存放的位置。
关键渲染路径是什么
浏览器拿到 HTML 后会解析 DOM,解析 CSS 得到 CSSOM,再合成 Render Tree,之后做 Layout 和 Paint。性能优化常见做法就是减少阻塞资源、缩短关键路径和降低重排重绘开销。
webpack、babel、AST 分别是什么
webpack 主要负责模块打包和构建流程组织,babel 主要负责语法转换,AST 是代码的抽象语法树表示。很多代码转换、静态分析和工程化能力,本质上都是围绕 AST 做处理。
两数之和怎么答
我会优先用哈希表。遍历数组时先看目标差值是否已经出现过,如果出现过就直接返回下标,否则把当前值和下标存进去。这样时间复杂度是
O(n)。
两个栈实现队列怎么答
一个栈负责入队,一个栈负责出队。出队时如果输出栈为空,就把输入栈全部倒过去,再弹栈顶。这样每个元素最多搬运一次,均摊复杂度是
O(1)。
数组洗牌怎么答
正确洗牌要用 Fisher-Yates。核心是从后往前遍历,每次在
[0, i]范围里随机选一个元素交换,这样才能保证每种排列概率一致。
反问面试官
- 这个岗位在前端侧更偏业务交互、平台建设,还是偏 React 工程和性能优化
- 团队对前端同学的期待更偏快速交付,还是对基础原理和工程质量也会有明确要求
- 这个岗位进去后最先接触的业务和技术难点通常是什么