Skip to content

面试

按场景分类的前端高频面试题 + 标准答案/思路


一、JavaScript 基础与原理(必问)

1. 数据类型与类型判断

Q1:JS 有哪些数据类型?

答:

分为原始类型引用类型

  • 原始类型(7 种):

undefinednullbooleannumberstringsymbolbigint

  • 引用类型:

object(包含对象、数组、函数、正则、日期等)

关键点:

  • 原始类型存于栈,值不可变;

  • 引用类型存于堆,栈中存引用地址。


Q2:typeof 和 instanceof 的区别?

答:

  • typeof:判断原始类型(除了 null),返回字符串。

    • typeof null'object'(历史 bug)

    • typeof []'object'

    • typeof function(){}'function'

  • instanceof:判断是否为某个构造函数的实例,基于原型链。

    • [] instanceof Arraytrue

    • [] instanceof Objecttrue

    • 不能判断原始类型:5 instanceof Numberfalse

最通用判断:

JavaScript

Object.prototype.toString.call(val)
// [object String] / [object Array] / [object Null]

Q3:== 和 === 区别?隐式转换规则?

答:

  • ===:严格相等,先判断类型,再判断值,不转换。

  • ==:宽松相等,先做类型转换,再比较值

常见转换规则:

  • null == undefinedtrue,但 null === undefinedfalse

  • 字符串与数字:字符串转数字

  • 布尔值:转数字 true→1false→0

  • 对象:转原始值(先 valueOf,再 toString

经典题:

JavaScript

[] == ![]   // true
// ![] → false → 0
// [] → 转原始值 '' → 0
// 0 == 0 → true

2. 作用域、闭包、this

Q4:什么是闭包?有什么用?有什么坑?

答:

定义:

闭包是指函数能够访问并保留其词法作用域,即使该函数在其定义的词法作用域之外执行。

原理:

外部函数执行完后,执行上下文本该销毁,但因为内部函数被引用,作用域链没有断,外部函数的 AO(活动对象)被保留。

应用场景:

  1. 创建私有变量/方法,避免全局污染

  2. 保存状态:循环绑定事件、防抖节流、React Hooks 保存状态

  3. 函数柯里化/偏函数

坑点:

  • 内存泄漏:闭包保留的变量不会被 GC,需要手动解除引用(如清除定时器、事件监听)

  • 循环中误用 var 导致共享同一变量(用 let/const 或闭包包裹)


Q5:this 指向有哪些情况?

答:

this 指向调用时决定,不是定义时。

  1. 默认绑定:全局/独立函数调用 → window(严格模式 undefined

  2. 隐式绑定:对象方法调用 → 调用对象

  3. 显式绑定call/apply/bind → 绑定的对象

  4. new 绑定new F() → 新创建的实例对象

  5. 箭头函数:继承外层作用域的 this,不绑定自己的 this

优先级:

new > 显式 > 隐式 > 默认


Q6:手写 call / apply / bind(至少会说思路)

思路(以 call 为例):

  1. 判断 this 是否为函数

  2. 将函数挂载到传入的上下文对象上

  3. 执行并拿到结果

  4. 删除挂载的属性,返回结果

简版代码:

JavaScript

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,这就是原型链

关系:

Plain

obj.__proto__ === Object.getPrototypeOf(obj) === 构造函数.prototype

Q8:JS 如何实现继承?最优方案?

答:

常见方式:

  1. 原型链继承:共享引用属性,无法传参

  2. 借用构造函数:可传参,但方法无法复用

  3. 组合继承:原型+构造函数,最常用,但父构造函数会执行两次

  4. 寄生组合继承:最优,只执行一次父构造函数

ES6 class extends 本质就是寄生组合继承的语法糖。


4. 异步与事件循环

Q9:什么是事件循环(Event Loop)?宏任务/微任务有哪些?

答:

JS 是单线程,为了不阻塞,用事件循环处理异步。

执行顺序:

  1. 执行同步代码

  2. 执行当前微任务队列清空

  3. 尝试渲染

  4. 取一个宏任务执行

  5. 回到 2

宏任务:

script 整体、setTimeoutsetInterval、I/O、UI 渲染、setImmediate(Node)

微任务:

Promise.then/catch/finallyasync/await 后面、MutationObserverprocess.nextTick(Node)

口诀:同步先跑,微任务清完,再跑一个宏任务。


Q10:Promise 有哪些状态?手写 Promise 思路?

答:

状态:pendingfulfilled / rejected,一旦改变不可再变。

手写思路:

  1. 构造函数里存状态、结果、成功/失败回调数组

  2. 实现 resolve/reject,改变状态并执行回调

  3. 实现 then,支持链式调用,返回新 Promise

  4. 处理值穿透、异常捕获


Q11:手写防抖 / 节流

防抖(debounce): 频繁触发只执行最后一次

JavaScript

function debounce(fn, delay) {
  let timer = null
  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

节流(throttle): 一定时间内只执行一次

JavaScript

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:你用过哪些设计模式?讲一个

答(以观察者模式为例):

定义: 一对多,主题变化自动通知所有依赖。

场景:

  • 事件系统 addEventListener

  • Vue/React 响应式更新

  • 状态管理库(Vuex、Redux)

简版实现思路:

  • 维护一个事件中心(对象)

  • on 存回调,emit 触发,off 取消


二、浏览器与网络(中高级必问)

1. 浏览器渲染流程

Q14:从输入 URL 到页面展示,发生了什么?

答:

  1. DNS 解析:域名 → IP

  2. TCP 连接:三次握手

  3. HTTPS 握手(如果是 https)

  4. 发送 HTTP 请求

  5. 服务器响应,返回 HTML

  6. 浏览器解析

    • 解析 HTML → DOM 树

    • 解析 CSS → CSSOM 树

    • 合成 → 渲染树(Render Tree)

  7. 布局(Layout/Reflow):计算几何信息

  8. 绘制(Paint/Repaint):填充像素

  9. 合成(Composite):图层合并,显示页面


Q15:重排(reflow)、重绘(repaint)、合成层?如何优化?

答:

  • 重排:修改几何属性(宽高、位置)→ 重新布局,开销大

  • 重绘:修改样式但不影响布局(颜色、背景)→ 只重画像素

  • 合成:transform/opacity 等在合成线程处理,不触发重排重绘

优化:

  1. 批量修改 DOM,使用文档片段

  2. 脱离文档流(absolute/fixed)减少影响范围

  3. 使用 transformopacity 做动画,开启硬件加速

  4. 避免读取布局属性后立即修改(避免强制同步布局)


2. 安全

Q16:什么是 XSS?如何防御?

答:

XSS(跨站脚本):攻击者注入恶意脚本,在用户浏览器执行。

类型:

  • 存储型:存入数据库,页面渲染时执行

  • 反射型:URL 参数带入,服务端直接输出

  • DOM 型:前端直接拼接 HTML 导致

防御:

  1. 转义:对用户输入/输出做 HTML 转义(<&lt;

  2. CSP(内容安全策略):限制资源加载和脚本执行

  3. HttpOnly Cookie:防止脚本读取 Cookie

  4. 避免 innerHTML/document.write 拼接用户数据


Q17:什么是 CSRF?如何防御?

答:

CSRF(跨站请求伪造):利用用户登录态,在第三方网站发起请求。

防御:

  1. Token 验证:表单/请求头带随机 Token

  2. SameSite CookieStrict/Lax 限制跨站携带 Cookie

  3. Referer/Origin 校验

  4. 敏感操作二次验证(验证码、密码)


3. 网络与性能

Q18:HTTP 缓存(强缓存、协商缓存)?

答:

强缓存:不发请求,直接用本地

  • Cache-Control: max-age=xxx

  • Expires(绝对时间,老方案)

协商缓存:发请求,服务器判断是否可用

  • 响应:ETag / Last-Modified

  • 请求:If-None-Match / If-Modified-Since

  • 304 则用缓存,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 不为 visible

  • float 不为 none

  • position: absolute/fixed

  • display: flex/grid/inline-block/table-cell

  • body 根元素

作用:

  1. 清除浮动(解决父元素高度塌陷)

  2. 避免外边距重叠

  3. 阻止元素被浮动元素覆盖


Q22:水平垂直居中方案(至少说 3 种)

答:

  1. flex(最常用)
CSS

.parent { display: flex; justify-content: center; align-items: center; }
  1. 绝对定位 + transform
CSS

.parent { position: relative; }
.child {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
}
  1. grid
CSS

.parent { display: grid; place-items: center; }
  1. 绝对定位 + margin: auto(宽高固定)

2. CSS 进阶

Q23:CSS 选择器优先级如何计算?

答:

权重四组:!important > 行内 > ID > 类/属性/伪类 > 元素/伪元素

计算规则:

  • 行内:1000

  • ID:100

  • 类/属性/伪类:10

  • 元素/伪元素:1

注意:

  • 权重相加,不进位

  • 后写的覆盖先写的(同权重)

  • !important 优先级最高,慎用


Q24:flex 常见属性?

答:

容器:

  • display: flex

  • flex-direction:主轴方向

  • justify-content:主轴对齐

  • align-items:交叉轴对齐

  • flex-wrap:是否换行

项目:

  • flex: 1flex-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 组件通信方式?

答:

  1. 父子:props / $emit

  2. 父子:v-model(语法糖)

  3. 爷孙/跨级:provide/inject

  4. 兄弟/全局:事件总线(Vue2)/ Pinia/Vuex

  5. 父访问子:ref / $parent / $children

  6. 插槽:默认插槽、具名插槽、作用域插槽


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 性能优化手段?

答:

  1. 组件懒加载:React.lazy + Suspense

  2. 避免不必要渲染:React.memouseMemouseCallback

  3. 列表用唯一 key,避免用 index

  4. 长列表用虚拟列表(react-window / react-virtualized)

  5. 合理拆分组件,减少重渲染范围


3. 工程化

Q31:Webpack 构建流程?

答:

  1. 初始化:读取配置,初始化 Compiler

  2. 编译:从入口开始,递归解析依赖

  3. 模块解析:loader 处理不同文件(babel、css-loader 等)

  4. 优化:tree shaking、代码分割、压缩

  5. 输出:生成 chunk,写入文件


Q32:什么是 Tree Shaking?前提?

答:

Tree Shaking:移除未使用代码,依赖ES Module 静态结构

前提:

  • 使用 ES Module(import/export

  • 生产模式,开启优化

  • 代码无副作用(配置 sideEffects


Q33:Vite 为什么比 Webpack 快?

答:

  • 开发模式:

    • 基于 ESBuild 预构建依赖

    • 浏览器原生 ES Module,按需请求,不打包

    • 热更新只更新改动模块,速度快

  • 生产模式:

    • 用 Rollup 打包,体积更小

五、项目与工程实践(重点准备“故事”)

Q34:讲一个你最有挑战的项目?

回答结构(STAR):

  1. S(背景):项目是什么,业务目标,技术栈

  2. T(任务):你负责什么,难点是什么

  3. A(行动):你做了什么方案,怎么落地,遇到什么问题怎么解决

  4. R(结果):数据/效果(性能提升、用户体验、业务指标)

建议:

准备 1~2 个,突出:性能优化、复杂交互、工程化、架构设计。


Q35:你做过哪些性能优化?

答:

可以按实际说,比如:

  • 图片优化:WebP、懒加载、响应式图片

  • 资源优化:代码分割、懒加载、压缩、缓存

  • 运行优化:长任务拆分、防抖节流、减少重渲染

  • 监控:接入 Sentry/自研监控,定位瓶颈


六、算法与手写题(高频)

Q36:手写深拷贝

思路:

  • 判断类型,原始类型直接返回

  • 处理循环引用(用 Map/WeakMap 记录)

  • 递归拷贝对象/数组

  • 处理函数、正则、日期等特殊对象

简版:

JavaScript

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

JavaScript

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:遇到需求不合理怎么办?

答:

  1. 先理解需求背后的业务目标

  2. 给出更优方案(技术/体验/成本角度)

  3. 与产品/设计对齐,达成共识

  4. 若必须做,评估风险,给出排期和影响


Q39:你最近在学什么?

答:

说真实的,比如:

  • 深入框架原理(Vue3/React 源码)

  • 工程化(Vite、微前端、低代码)

  • 跨端(Taro/Uniapp、Electron)

  • 性能与监控体系


Q40:为什么想来我们公司?

答:

  • 业务/技术栈匹配

  • 团队技术氛围好

  • 想在某方向深入,公司平台能提供机会

  • 个人成长与公司发展匹配


最近更新