前端面试八股文:JS 原理、框架与安全

FreeGuideOnline 最新 2026-06-18

前端面试八股文:JS 原理、框架与安全

从记忆到理解,本教程将带你深入前端面试中的“经典八股题”,覆盖 JavaScript 运行机制、现代框架核心思想以及必备的 Web 安全知识。结构清晰、案例丰富,助你从根源上搞懂每一个考点,实现举一反三。

JavaScript 核心原理

这部分将剖析面试中高频出现的 JS 底层机制,告别死记硬背,建立稳固的执行上下文与内存模型。

执行上下文与调用栈

理解代码运行时的环境创建与销毁过程,是解读作用域、闭包和异步行为的钥匙。

  • 执行上下文(Execution Context) 任何 JavaScript 代码都是在执行上下文中运行的。主要有三种类型:

    • 全局执行上下文:代码首次运行时创建,一个程序中只有一个。
    • 函数执行上下文:每当一个函数被调用时,都会创建一个新的上下文。
    • Eval 执行上下文eval() 函数内部的代码,极少使用。
  • 执行上下文的生命周期

    1. 创建阶段
      • 生成变量对象(Variable Object,VO),包含 arguments、函数声明(整体提升)、变量声明(var 提升但未赋值,值为 undefined)。
      • 建立作用域链(Scope Chain)。
      • 确定 this 的指向。
    2. 执行阶段
      • 变量赋值、函数引用、执行代码。
    3. 销毁阶段
      • 执行完毕,上下文出栈,变量对象被回收。
  • 调用栈(Call Stack) 一种 LIFO(后进先出)的数据结构,用于管理函数的调用关系。每当函数被调用,其上下文被推入栈顶;当函数执行结束,栈顶上下文弹出。栈溢出(Stack Overflow)通常由递归过深引起。

常见面试题:解释一下变量提升和函数提升的区别。

  • 函数声明会被整体提升(函数名和函数体),可以在声明前调用。
  • var 声明变量提升但值为 undefinedlet/const 存在暂时性死区,不会提前初始化。

作用域与闭包

闭包是 JavaScript 中的难点与高频考点,其根本在于词法作用域和垃圾回收。

  • 词法作用域(静态作用域) 函数的作用域在定义时就已经决定,与调用位置无关。寻找变量时,从自身作用域开始,逐级向上层作用域查找,直到全局。

  • 闭包(Closure) 一个函数和对其周围状态(词法环境)的引用捆绑在一起,这样的组合就是闭包。简单说,一个内部函数能够访问其外部函数的作用域,即使外部函数已经执行完毕。

    function outer() {
      let count = 0;
      return function inner() {
        count++;
        console.log(count);
      };
    }
    const counter = outer();
    counter(); // 1
    counter(); // 2
    

    闭包的本质:内部函数被返回并在外部持有引用,导致外部函数的词法环境无法被垃圾回收。 应用场景:模块化、封装私有变量、函数柯里化、防抖节流。 内存泄漏风险:不合理地维持闭包会导致变量常驻内存,应在不再需要时手动解除引用(如 counter = null)。

原型与原型链

JavaScript 依靠原型实现继承,这是面试中的基础考查点。

  • 构造函数与原型对象

    • 每个函数都有一个 prototype 属性,指向一个对象(原型对象)。
    • 原型对象上有一个 constructor 属性,指回构造函数本身。
    • 构造函数创建的实例,通过 __proto__(或 Object.getPrototypeOf)指向构造函数的 prototype
  • 原型链 访问对象的属性时,先在自身查找;若没有,则沿 __proto__ 向上查找,直到 Object.prototype,最后为 null。这条查找链路就是原型链。

  • 继承方式

    • 原型链继承:子类原型指向父类实例,缺点:引用值共享、无法传参。
    • 借用构造函数:用 call 调用父类构造,缺点:方法无法复用。
    • 组合继承:原型链继承方法,借用构造函数继承属性,但会调用两次父类构造。
    • 寄生组合继承(最优):通过创建父类原型的副本并修正 constructor 来实现,ES6 class extends 的本质。

面试高频:手写 instanceof 的实现。 思路:沿着左边对象的原型链 __proto__ 查找,判断是否等于右边构造函数的 prototype

事件循环与异步编程

前端必须掌握 JavaScript 的单线程模型与异步调度策略。

  • 宏任务与微任务

    • 宏任务(Macro Task)script(整体代码)、setTimeoutsetInterval、I/O、UI 渲染等。
    • 微任务(Micro Task)Promise.then/catch/finallyMutationObserverqueueMicrotask
    • 执行顺序:每次宏任务执行完毕后,立即清空当前微任务队列中的所有微任务,然后再从宏任务队列中取下一个任务。
  • 事件循环流程

    1. 执行同步代码,这本身是一个宏任务。
    2. 当宏任务执行完毕,检查微任务队列并依次执行。
    3. 有需要则进行窗口渲染。
    4. 取下一个宏任务,重复上述循环。
  • Async/Await 本质是 Generator 和 Promise 的语法糖。async 函数返回一个 Promise;await 后面的表达式会等待 Promise 状态变化后的值,下面的代码会被放入微任务队列(相当于 Promise.then)。

常见考题:请写出 setTimeoutPromiseasync/await 混用时的输出顺序。这需要彻底理解微任务在 await 处的拆分。

现代框架核心概念

以 React 和 Vue 为例,深入它们的渲染机制、响应式原理和设计模式。面试官考核的不是 API 记忆,而是“为什么这么设计”。

虚拟 DOM 与 Diff 算法

操作真实 DOM 成本高昂,虚拟 DOM 提供了高效的更新策略。

  • 虚拟 DOM 是什么 用 JavaScript 对象来描述真实 DOM 节点的层级与属性。如 { tag: 'div', props: { id: 'app' }, children: [...] }
  • 渲染流程
    • 模板/JSX 编译为渲染函数,执行后生成虚拟 DOM 树。
    • 状态变化时,生成新的虚拟 DOM 树。
    • 通过 Diff 算法比较新旧两棵树,找出需更新的最小差异(Patch)。
    • 将 Patch 应用到真实 DOM。
  • 核心 Diff 策略
    • 同层比较:只比较同一层级节点,跨层级移动视为删除和新增。
    • 类型判断:节点类型不同则直接替换整棵子树。
    • Key 的重要性:在同层级子节点列表中,通过 key 来标识节点,实现最小移动和复用。Key 应稳定、唯一,避免用索引。
  • Vue 与 React 的差异
    • React:递归比对,强调纯函数,更新粒度在组件树。
    • Vue:编译阶段标记静态节点和动态绑定,比对时跳过静态内容;更新粒度在组件级别,并采用“靶向更新”。

响应式原理

数据驱动视图,必须理解数据变化如何自动触发视图更新。

  • Vue 2.x Object.defineProperty
    • 遍历每个属性,通过 getter 进行依赖收集,setter 触发更新。
    • 局限性:无法侦测对象属性的添加/删除、数组索引赋值和长度变化(因此需要 Vue.set/delete)。
  • Vue 3.x Proxy
    • 直接代理整个对象,可以拦截包括属性读取、新增、删除在内的 13 种操作。
    • 天然支持数组索引和 length 变化,也无需递归到对象的每一个属性。
    • Reflect 配合使用保证 this 指向正确。
  • React 的不可变数据
    • React 通过状态引用变化来触发更新(如 setState 传入新对象)。
    • 数据是不可变的,内部使用 Object.is 做浅比较判断是否需要重新计算子树。
    • 核心思想:让数据可变(Vue)带来便利,但也可能带来追踪负担;数据不可变(React)让变化可预测,但需要编写更多更新逻辑。

组件化与设计模式

现代框架的组件设计思想贯穿于整个面试。

  • 单向数据流 父组件通过 props 传递数据给子组件,子组件不能直接修改 props。要改变数据,必须通过回调函数向父组件传递事件,由父组件更新状态。这保证了数据变化可追踪。
  • 状态提升 当多个组件需要共享同一份状态时,将该状态移动到它们最近的共同父组件中管理。
  • 组合优于继承 React 强烈的哲学:通过 children、Render Props、HOC 等组合方式复用逻辑,而不是创建深层组件继承层级。Vue 也推崇插槽(slot)和混入/组合式函数。
  • 高阶组件(HOC)与 Hooks
    • HOC:一个函数,接收组件并返回新组件,用于逻辑复用。可能导致“包装地狱”。
    • React Hooks:在函数组件里“钩入”状态和生命周期,使逻辑复用更轻量(自定义 Hook)。
    • Vue 3 Composition API:与 Hooks 理念类似,通过 setup 函数聚合相关逻辑,取代 Options API 的碎片化倾向。

前端安全实践

安全不仅是常识,更是区分高级工程师的关键模块。面试中常考的几种攻击与防御必须滚瓜烂熟。

XSS 跨站脚本攻击

攻击者在目标网站注入恶意脚本,当用户浏览时脚本执行。

  • 攻击类型
    • 存储型 XSS:恶意脚本永久存储在服务器(数据库、留言板等),用户访问页面时执行。危害最大。
    • 反射型 XSS:脚本包含在 URL 参数中,服务器将其反射回响应。需要诱导用户点击链接。
    • DOM 型 XSS:纯客户端漏洞,通过修改页面 DOM 节点注入脚本(如 innerHTMLdocument.write 接收不干净数据)。
  • 防御手段
    • 输出编码:根据输出位置(HTML 标签、属性、JavaScript 变量、CSS、URL),对数据进行相应转义处理。
    • 内容安全策略(CSP):通过 HTTP 头部或 meta 标签,定义允许加载资源的域,禁止内联脚本的执行。
    • HttpOnly Cookie:设置 Cookie 为 HttpOnly,使得 JavaScript 无法通过 document.cookie 读取,减少 cookie 泄露。
    • 输入验证:前后端都应对用户输入做长度、格式限制,拒绝可疑内容。
    • React/Vue 自动转义:框架在 JSX/模板中输出变量时默认进行 HTML 转义。但需注意 dangerouslySetInnerHTML(React)和 v-html(Vue)的使用,避免传入用户可控数据。

CSRF 跨站请求伪造

劫持已认证用户的浏览器发送恶意请求给受信任的站点。

  • 攻击原理 用户登录了银行网站 A,Cookie 保存在浏览器中。在未退出 A 的情况下访问恶意网站 B。B 站包含一个请求,比如 <img src="http://bank.com/transfer?amount=1000&to=hacker">。浏览器带着 A 的 Cookie 发出请求,银行误以为用户授权操作。
  • 防御策略
    • Referer / Origin 校验:检测请求头中的来源是否为可信域名,但可能被篡改或缺失。
    • Token 校验:服务端生成一个随机 CSRF Token,嵌入在页面的表单或请求头中,每次请求必须携带 Token,并验证。Token 不与 Cookie 自动发送。
    • SameSite Cookie:设置 Cookie 属性 SameSite=StrictLax,限制跨站请求携带 Cookie。Strict 完全禁止,Lax 允许部分(如链接跳转)携带。
    • 验证码/二次验证:对敏感操作强制要求用户交互。

其他常见安全问题

  • 点击劫持(Clickjacking) 攻击者使用透明的 iframe 覆盖在诱人点击的按钮上,用户实际点击了隐藏页面中的按钮。
    • 防御:通过 X-Frame-Options 头部(DENY / SAMEORIGIN)或 CSP 的 frame-ancestors 指令,禁止页面被放入 iframe。
  • 中间人攻击与 HTTPS HTTP 明文传输,攻击者可窃听、篡改通信内容。
    • 升级 HTTPS:使用 TLS 加密,验证服务器身份,保证数据机密性与完整性。
    • HSTS:强制客户端使用 HTTPS 连接,避免 SSLstrip 降级攻击。
  • 依赖安全 通过 npm audit 等工具扫描项目依赖的已知漏洞,及时升级和修复。

以上涵盖了前端面试中 JS 原理、框架核心与安全三大支柱的“八股文”内容。真正的掌握不是背诵,而是结合自身项目经验,将这些知识点串联成体系。建议结合手写代码、模拟场景深入理解每个原理背后的设计逻辑,让面试官看到解决问题时的扎实功底。