面试
按场景分类的前端高频面试题 + 标准答案/思路
一、JavaScript 基础与原理(必问)
1. 数据类型与类型判断
Q1:JS 有哪些数据类型?
答:
分为原始类型和引用类型。
- 原始类型(7 种):
undefined、null、boolean、number、string、symbol、bigint
- 引用类型:
object(包含对象、数组、函数、正则、日期等)
关键点:
原始类型存于栈,值不可变;
引用类型存于堆,栈中存引用地址。
Q2:typeof 和 instanceof 的区别?
答:
typeof:判断原始类型(除了null),返回字符串。typeof null→'object'(历史 bug)typeof []→'object'typeof function(){}→'function'
instanceof:判断是否为某个构造函数的实例,基于原型链。[] instanceof Array→true[] instanceof Object→true不能判断原始类型:
5 instanceof Number→false
最通用判断:
Object.prototype.toString.call(val)
// [object String] / [object Array] / [object Null]Q3:== 和 === 区别?隐式转换规则?
答:
===:严格相等,先判断类型,再判断值,不转换。==:宽松相等,先做类型转换,再比较值。
常见转换规则:
null == undefined→true,但null === undefined→false字符串与数字:字符串转数字
布尔值:转数字
true→1、false→0对象:转原始值(先
valueOf,再toString)
经典题:
[] == ![] // true
// ![] → false → 0
// [] → 转原始值 '' → 0
// 0 == 0 → true2. 作用域、闭包、this
Q4:什么是闭包?有什么用?有什么坑?
答:
定义:
闭包是指函数能够访问并保留其词法作用域,即使该函数在其定义的词法作用域之外执行。
原理:
外部函数执行完后,执行上下文本该销毁,但因为内部函数被引用,作用域链没有断,外部函数的 AO(活动对象)被保留。
应用场景:
创建私有变量/方法,避免全局污染
保存状态:循环绑定事件、防抖节流、React Hooks 保存状态
函数柯里化/偏函数
坑点:
内存泄漏:闭包保留的变量不会被 GC,需要手动解除引用(如清除定时器、事件监听)
循环中误用 var 导致共享同一变量(用 let/const 或闭包包裹)
Q5:this 指向有哪些情况?
答:
this 指向调用时决定,不是定义时。
默认绑定:全局/独立函数调用 →
window(严格模式undefined)隐式绑定:对象方法调用 → 调用对象
显式绑定:
call/apply/bind→ 绑定的对象new 绑定:
new F()→ 新创建的实例对象箭头函数:继承外层作用域的 this,不绑定自己的 this
优先级:
new > 显式 > 隐式 > 默认
Q6:手写 call / apply / bind(至少会说思路)
思路(以 call 为例):
判断
this是否为函数将函数挂载到传入的上下文对象上
执行并拿到结果
删除挂载的属性,返回结果
简版代码:
Function.prototype.myCall = function (context, ...args) {
context = context || window
const fn = Symbol()
context[fn] = this
const res = context[fn](...args)
delete context[fn]
return res
}apply只是参数是数组bind返回一个新函数,可柯里化,new时忽略绑定的 this
3. 原型与继承
Q7:原型、原型链是什么?
答:
每个对象都有
__proto__,指向其构造函数的prototype每个函数都有
prototype,它是一个对象,包含constructor和共享方法当访问对象属性时,先找自身,找不到就沿
__proto__往上找,直到null,这就是原型链
关系:
obj.__proto__ === Object.getPrototypeOf(obj) === 构造函数.prototypeQ8:JS 如何实现继承?最优方案?
答:
常见方式:
原型链继承:共享引用属性,无法传参
借用构造函数:可传参,但方法无法复用
组合继承:原型+构造函数,最常用,但父构造函数会执行两次
寄生组合继承:最优,只执行一次父构造函数
ES6 class extends 本质就是寄生组合继承的语法糖。
4. 异步与事件循环
Q9:什么是事件循环(Event Loop)?宏任务/微任务有哪些?
答:
JS 是单线程,为了不阻塞,用事件循环处理异步。
执行顺序:
执行同步代码
执行当前微任务队列清空
尝试渲染
取一个宏任务执行
回到 2
宏任务:
script 整体、setTimeout、setInterval、I/O、UI 渲染、setImmediate(Node)
微任务:
Promise.then/catch/finally、async/await 后面、MutationObserver、process.nextTick(Node)
口诀:同步先跑,微任务清完,再跑一个宏任务。
Q10:Promise 有哪些状态?手写 Promise 思路?
答:
状态:pending → fulfilled / rejected,一旦改变不可再变。
手写思路:
构造函数里存状态、结果、成功/失败回调数组
实现
resolve/reject,改变状态并执行回调实现
then,支持链式调用,返回新 Promise处理值穿透、异常捕获
Q11:手写防抖 / 节流
防抖(debounce): 频繁触发只执行最后一次
function debounce(fn, delay) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}节流(throttle): 一定时间内只执行一次
function throttle(fn, interval) {
let last = 0
return function (...args) {
const now = Date.now()
if (now - last >= interval) {
last = now
fn.apply(this, args)
}
}
}5. 模块化与设计模式
Q12:CommonJS 和 ES Module 区别?
答:
CommonJS:
运行时加载,同步
module.exports/require值拷贝,导出基本类型是复制,导出对象是引用
用于 Node
ES Module:
编译时加载,静态分析
export/import动态引用(live binding),原模块变了导入也变
用于浏览器/Node 现代环境
Q13:你用过哪些设计模式?讲一个
答(以观察者模式为例):
定义: 一对多,主题变化自动通知所有依赖。
场景:
事件系统
addEventListenerVue/React 响应式更新
状态管理库(Vuex、Redux)
简版实现思路:
维护一个事件中心(对象)
on存回调,emit触发,off取消
二、浏览器与网络(中高级必问)
1. 浏览器渲染流程
Q14:从输入 URL 到页面展示,发生了什么?
答:
DNS 解析:域名 → IP
TCP 连接:三次握手
HTTPS 握手(如果是 https)
发送 HTTP 请求
服务器响应,返回 HTML
浏览器解析:
解析 HTML → DOM 树
解析 CSS → CSSOM 树
合成 → 渲染树(Render Tree)
布局(Layout/Reflow):计算几何信息
绘制(Paint/Repaint):填充像素
合成(Composite):图层合并,显示页面
Q15:重排(reflow)、重绘(repaint)、合成层?如何优化?
答:
重排:修改几何属性(宽高、位置)→ 重新布局,开销大
重绘:修改样式但不影响布局(颜色、背景)→ 只重画像素
合成:transform/opacity 等在合成线程处理,不触发重排重绘
优化:
批量修改 DOM,使用文档片段
脱离文档流(absolute/fixed)减少影响范围
使用
transform、opacity做动画,开启硬件加速避免读取布局属性后立即修改(避免强制同步布局)
2. 安全
Q16:什么是 XSS?如何防御?
答:
XSS(跨站脚本):攻击者注入恶意脚本,在用户浏览器执行。
类型:
存储型:存入数据库,页面渲染时执行
反射型:URL 参数带入,服务端直接输出
DOM 型:前端直接拼接 HTML 导致
防御:
转义:对用户输入/输出做 HTML 转义(
<→<)CSP(内容安全策略):限制资源加载和脚本执行
HttpOnly Cookie:防止脚本读取 Cookie
避免
innerHTML/document.write拼接用户数据
Q17:什么是 CSRF?如何防御?
答:
CSRF(跨站请求伪造):利用用户登录态,在第三方网站发起请求。
防御:
Token 验证:表单/请求头带随机 Token
SameSite Cookie:
Strict/Lax限制跨站携带 CookieReferer/Origin 校验
敏感操作二次验证(验证码、密码)
3. 网络与性能
Q18:HTTP 缓存(强缓存、协商缓存)?
答:
强缓存:不发请求,直接用本地
Cache-Control: max-age=xxxExpires(绝对时间,老方案)
协商缓存:发请求,服务器判断是否可用
响应:
ETag/Last-Modified请求:
If-None-Match/If-Modified-Since304 则用缓存,200 则返回新内容
优先级:
Cache-Control > Expires
ETag > Last-Modified
Q19:HTTP1.1 / HTTP2 / HTTP3 区别?
答:
HTTP1.1:
队头阻塞
长连接、管道化(支持有限)
文本协议,头信息大
HTTP2:
二进制分帧
多路复用(同连接多流,解决队头阻塞)
头部压缩(HPACK)
服务器推送
HTTP3:
基于 QUIC(UDP)
解决 TCP 队头阻塞
0-RTT 握手,更快
Q20:前端性能优化方案(按加载/运行/渲染)
答:
加载阶段:
减少请求:合并资源、雪碧图、接口合并
压缩:JS/CSS/HTML 压缩,图片压缩(WebP/AVIF)
缓存:HTTP 缓存、本地存储
懒加载:图片懒加载、路由懒加载、组件懒加载
预加载:
preload/prefetch/dns-prefetch
运行阶段:
长任务拆分,使用 Web Worker
避免内存泄漏(定时器、事件监听、闭包)
防抖节流处理高频事件
渲染阶段:
减少重排重绘,用 transform/opacity
虚拟列表处理长列表
骨架屏、Suspense 提升体验
三、CSS 与布局
1. 布局与 BFC
Q21:什么是 BFC?如何触发?有什么用?
答:
BFC(块级格式化上下文):一块独立渲染区域,内部布局不影响外部。
触发方式:
overflow不为visiblefloat不为noneposition: absolute/fixeddisplay: flex/grid/inline-block/table-cellbody根元素
作用:
清除浮动(解决父元素高度塌陷)
避免外边距重叠
阻止元素被浮动元素覆盖
Q22:水平垂直居中方案(至少说 3 种)
答:
- flex(最常用)
.parent { display: flex; justify-content: center; align-items: center; }- 绝对定位 + transform
.parent { position: relative; }
.child {
position: absolute;
left: 50%; top: 50%;
transform: translate(-50%, -50%);
}- grid
.parent { display: grid; place-items: center; }- 绝对定位 + margin: auto(宽高固定)
2. CSS 进阶
Q23:CSS 选择器优先级如何计算?
答:
权重四组:!important > 行内 > ID > 类/属性/伪类 > 元素/伪元素
计算规则:
行内:1000
ID:100
类/属性/伪类:10
元素/伪元素:1
注意:
权重相加,不进位
后写的覆盖先写的(同权重)
!important优先级最高,慎用
Q24:flex 常见属性?
答:
容器:
display: flexflex-direction:主轴方向justify-content:主轴对齐align-items:交叉轴对齐flex-wrap:是否换行
项目:
flex: 1→flex-grow:1; flex-shrink:1; flex-basis:0%align-self:单独控制交叉轴
四、框架与工程化(Vue/React 二选一)
1. Vue 高频
Q25:Vue2 / Vue3 响应式原理?
答:
Vue2:
Object.defineProperty劫持对象属性的get/set缺点:无法监听新增/删除属性、数组下标/长度,需用
$set/$delete和重写数组方法
Vue3:
Proxy代理整个对象,可监听新增/删除、数组下标配合
Reflect操作,更完整懒代理,性能更好
Q26:Vue 组件通信方式?
答:
父子:
props/$emit父子:
v-model(语法糖)爷孙/跨级:
provide/inject兄弟/全局:事件总线(Vue2)/ Pinia/Vuex
父访问子:
ref/$parent/$children插槽:默认插槽、具名插槽、作用域插槽
Q27:Vue3 相比 Vue2 有哪些优化?
答:
响应式:Proxy 替代 defineProperty
编译:PatchFlag 标记动态节点,只 diff 动态部分
体积:Tree-shaking 友好,按需引入
语法:Composition API 替代 Options API,逻辑复用更清晰
其他:Teleport、Suspense、Fragment、更好的 TS 支持
2. React 高频
Q28:React Hooks 为什么不能在条件/循环中使用?
答:
React 按调用顺序记录 Hooks 状态。
如果在条件/循环中,顺序会变,导致状态错乱。
所以必须在函数顶层使用,保证每次渲染顺序一致。
Q29:useEffect 执行时机?依赖数组规则?
答:
不传依赖:每次渲染后执行
空数组
[]:仅挂载和卸载时执行(类似 componentDidMount + componentWillUnmount)传依赖:依赖变化时执行
注意:
依赖要写全,否则会闭包陷阱
函数/对象依赖要用
useCallback/useMemo缓存
Q30:React 性能优化手段?
答:
组件懒加载:
React.lazy+Suspense避免不必要渲染:
React.memo、useMemo、useCallback列表用唯一 key,避免用 index
长列表用虚拟列表(react-window / react-virtualized)
合理拆分组件,减少重渲染范围
3. 工程化
Q31:Webpack 构建流程?
答:
初始化:读取配置,初始化 Compiler
编译:从入口开始,递归解析依赖
模块解析:loader 处理不同文件(babel、css-loader 等)
优化:tree shaking、代码分割、压缩
输出:生成 chunk,写入文件
Q32:什么是 Tree Shaking?前提?
答:
Tree Shaking:移除未使用代码,依赖ES Module 静态结构。
前提:
使用 ES Module(
import/export)生产模式,开启优化
代码无副作用(配置
sideEffects)
Q33:Vite 为什么比 Webpack 快?
答:
开发模式:
基于 ESBuild 预构建依赖
浏览器原生 ES Module,按需请求,不打包
热更新只更新改动模块,速度快
生产模式:
- 用 Rollup 打包,体积更小
五、项目与工程实践(重点准备“故事”)
Q34:讲一个你最有挑战的项目?
回答结构(STAR):
S(背景):项目是什么,业务目标,技术栈
T(任务):你负责什么,难点是什么
A(行动):你做了什么方案,怎么落地,遇到什么问题怎么解决
R(结果):数据/效果(性能提升、用户体验、业务指标)
建议:
准备 1~2 个,突出:性能优化、复杂交互、工程化、架构设计。
Q35:你做过哪些性能优化?
答:
可以按实际说,比如:
图片优化:WebP、懒加载、响应式图片
资源优化:代码分割、懒加载、压缩、缓存
运行优化:长任务拆分、防抖节流、减少重渲染
监控:接入 Sentry/自研监控,定位瓶颈
六、算法与手写题(高频)
Q36:手写深拷贝
思路:
判断类型,原始类型直接返回
处理循环引用(用 Map/WeakMap 记录)
递归拷贝对象/数组
处理函数、正则、日期等特殊对象
简版:
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj
if (map.has(obj)) return map.get(obj)
let cloneObj = Array.isArray(obj) ? [] : {}
map.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
cloneObj[key] = deepClone(obj[key], map)
}
}
return cloneObj
}Q37:手写 Promise.all
思路:
接收可迭代对象,返回新 Promise
记录成功数和结果数组
全部成功 resolve,有一个失败 reject
function promiseAll(promises) {
return new Promise((resolve, reject) => {
const results = []
let count = 0
const len = promises.length
if (len === 0) return resolve(results)
promises.forEach((p, index) => {
Promise.resolve(p).then(res => {
results[index] = res
count++
if (count === len) resolve(results)
}).catch(reject)
})
})
}七、软技能与综合题
Q38:遇到需求不合理怎么办?
答:
先理解需求背后的业务目标
给出更优方案(技术/体验/成本角度)
与产品/设计对齐,达成共识
若必须做,评估风险,给出排期和影响
Q39:你最近在学什么?
答:
说真实的,比如:
深入框架原理(Vue3/React 源码)
工程化(Vite、微前端、低代码)
跨端(Taro/Uniapp、Electron)
性能与监控体系
Q40:为什么想来我们公司?
答:
业务/技术栈匹配
团队技术氛围好
想在某方向深入,公司平台能提供机会
个人成长与公司发展匹配