← 返回题库
前端核心·真题演练

前端真题 130 题 · 架构 / OS / 手写 / 综合

浏览器与网络补遗、设计模式与架构、操作系统、JS 手写题(含 Vue keep-alive)、HTTP/JS 继承/浏览器工作原理综合收尾,109-130 题。

设计模式操作系统手写题HTTP浏览器工作原理

七、浏览器与网络(补遗)

说明:第 106、107 题在原题表中位于 Node.js 与设计模式之间,主题分属浏览器内存与网络,故单独成章。复习时建议与「一、HTML & 浏览器基础」的渲染/进程章节、「六、Node.js」的内存管理对照阅读。

106. 内存泄漏

定义:本应被 GC 回收的对象因仍被可达引用而无法回收 → 内存持续增长。(GC 用的是可达性分析,不是引用计数——所以"循环引用"本身不会泄漏,只有"从根可达但业务上已无用"才会。)

常见来源(按面试被问频率排序):

  1. 未清理的定时器 / 事件监听器(组件销毁时要 clearInterval、removeEventListener;React 里 useEffect 返回 cleanup,Vue 里 onUnmounted);
  2. 订阅未退订(EventBus、store.subscribe、WebSocket、MutationObserver);
  3. 闭包长期持有大对象(如把整个 response 挂进闭包,只用到其中一个字段);
  4. 意外的全局变量(未声明赋值、this 指向 window);
  5. 脱离文档的 DOM 引用(已 remove 但 JS 变量仍指向它);
  6. 无上限的缓存 / Map(要配 LRU 或 WeakMap——WeakMap 的 key 是弱引用,不阻止回收)。

排查步骤(能说出步骤才是真做过):

  1. Chrome DevTools → Memory 面板;
  2. 操作前打一次 Heap Snapshot
  3. 反复执行"进入/退出该页面"多次;
  4. 再打快照,选 Comparison 视图对比 Delta,按增量倒序找可疑对象;
  5. 点开对象看 Retainers(保留树) ——它回答"谁在引用我",是定位泄漏源的关键
  6. 配合 Performance Monitor 看 JS heap size 曲线是否持续上升不回落(这是最直观的判据)。

考点:归因到"可达性分析仍可达";定时器/监听器最常考;能说出"快照对比 + Retainers"就比只会背条目高一个层级。


107. HTTPS

HTTPS = HTTP + TLS/SSL:提供加密 + 身份认证 + 完整性

TLS 1.2 握手

  1. ClientHello(版本、套件、随机数);
  2. ServerHello + 证书(含公钥);
  3. 客户端验证证书(信任链到 CA、域名、有效期),生成预主密钥加密发回(或 ECDHE);
  4. 双方派生会话密钥(对称);
  5. 之后数据用对称加密(AES,快);握手用非对称/ECDHE 协商。

要点:混合加密(非对称传密钥/认证 + 对称传数据);摘要保完整性;证书 = "公钥 + CA 签名";TLS 1.3 握手 1-RTT、0-RTT 恢复。

考点:混合加密原因;证书链与中间人防御;与 HTTP/2/3 关系。

TLS 1.2 握手图解

sequenceDiagram
    autonumber
    participant C as 客户端
    participant S as 服务器
    participant CA as CA(证书颁发机构)

    C->>S: ① ClientHello(支持的 TLS 版本 / 加密套件 / 随机数 Random1)
    S->>C: ② ServerHello(选定的套件 / 随机数 Random2)+ 证书
    Note over C: ③ 校验证书链<br/>CA 签名是否有效 → 域名是否匹配 → 是否过期
    C->>CA: (本地或离线)验证证书信任链
    CA-->>C: 证书可信 ✅
    Note over C,S: ④ 密钥交换(ECDHE 或 RSA 加密预主密钥)
    C->>S: 发送用服务器公钥加密的预主密钥 / ECDHE 参数
    Note over C,S: ⑤ 双方用 Random1 + Random2 + 预主密钥 派生出会话密钥
    C->>S: ⑥ Finished(用会话密钥加密,验证握手完整性)
    S->>C: ⑦ Finished
    Note over C,S: 后续数据全部用<b>对称加密</b>(AES)传输

读图要点混合加密是本图的核心——握手用非对称/ECDHE(慢但安全地协商出密钥),传输用对称加密(快)。
加分点:TLS 1.3 把握手压缩到 1-RTT(甚至 0-RTT 恢复);同时废弃了 RSA 密钥交换,只保留前向安全的 (EC)DHE;对称部分默认用 AES-GCM 或 ChaCha20-Poly1305。


八、设计模式与架构

108. 常用设计模式

前端高频设计模式(讲清"意图 + 场景 + 例子"):

  1. 单例模式:全局唯一——Vuex/Pinia store、Redux store、全局弹窗、缓存。ESM 模块级变量天然单例;
  2. 观察者 / 发布订阅:事件总线 EventBus、Vue 响应式、addEventListener。观察者(目标直接通知观察者)vs 发布订阅(事件中心解耦);
  3. 工厂模式:批量创建对象——React.createElement、消息类型分发;
  4. 策略模式:算法族可替换——表单校验策略集、支付方式,消除大 if/switch;
  5. 装饰器模式:动态增强——HOC、mixin、中间件;
  6. 代理模式:控制访问——事件委托、图片懒加载代理、防抖节流、Vue3 Proxy;
  7. 适配器模式:兼容接口——axios 适配浏览器/node、浏览器差异抹平;
  8. 迭代器模式:for...of、Generator;
  9. 职责链模式:请求沿链传递——express/koa 中间件、事件冒泡;
  10. 命令模式:操作对象化——撤销/重做。

考点:说 3-5 个能举真实例子的;对比易混项;本质是"应对变化的复用方案",结合函数式/hooks 讲更显深度。


109. 重构(Refactoring)

重构不改变外部行为前提下改进内部结构。核心纪律:小步 + 行为不变 + 测试护航

手法:提炼函数/变量、消重复、拆过长函数、改命名;拆分 God Object、模块边界整理;拆分大组件、提取 hooks;接口隔离、依赖注入。

正确姿势

  1. 先有测试做安全网,每小步跑测试;
  2. 一次只做一种重构,频繁提交(可回滚);
  3. 不混业务改动;用 Lint/TS 辅助。

考点:强调"行为不变 + 小步 + 测试";"重构 vs 重写"取舍。


110. MVVM

MVVM = Model-View-ViewModel

  • Model:数据与业务(不碰 DOM);
  • View:视图/模板;
  • ViewModel:胶水层,数据绑定让 View 与 VM 自动同步,免手动 DOM 操作。

前端实现:Vue(data + 模板 + methods)、React(受控组件 + 单向数据流,常归"类 MVVM")。核心是数据驱动视图(声明式 UI)。

单向 vs 双向:Vue v-model 是语法糖(:value + @input)实现双向;React 单向受控,状态来源明确易调试。

考点:MVVM 结构图与绑定方向;与 MVC/MVP 的"谁驱动谁"区别。


111. MVC

MVC = Model-View-Controller

  • Model:数据与业务规则;
  • View:展示;
  • Controller:接收输入,协调 Model 与 View。

传统 Web MVC(后端):路由 → Controller → Model → 渲染 View 返回 HTML。
前端 MVC(Backbone):View 监听 DOM 事件 → Controller → 更新 Model → View 重渲染。

缺点:前端场景 View 与 Model 同步需大量手动代码 → 演进出 MVVM 数据绑定。

考点:前端 MVC 中 View 也监听事件职责重叠——演进动力。


112. MVP

MVP = Model-View-PresenterView 与 Model 完全解耦,Presenter 是唯一中间层:

  • View:只渲染 + 把事件交给 Presenter(被动视图,接口化,不含业务);
  • Presenter:持 View 接口与 Model,处理交互、更新数据与视图;
  • Model:纯数据/业务。

对比

模式View 与 Model驱动者绑定
MVC观察者关联Controller
MVP完全解耦Presenter(可测)手动同步
MVVM解耦ViewModel自动绑定

优点:Presenter 不依赖真实 DOM,mock View 即可单测(Android 常用)。缺点:胶水代码多。

考点:MVP 的 View 是被动的(passive view),与 MVC 中 View 主动观察 Model 的本质差异。


九、操作系统

113. 进程通信(操作系统层)

进程间通信方式:

  1. 管道:半双工字节流(亲缘进程);命名管道 FIFO(无亲缘);
  2. 消息队列:内核维护消息链表,传结构化消息;
  3. 共享内存最快,直接读写同一物理内存;需信号量同步;
  4. 信号量:计数器 + P/V 操作,解决互斥同步;
  5. 信号:异步通知(SIGINT/SIGTERM),携带信息少;
  6. 套接字 Socket:跨主机/本地(TCP/UDP/Unix socket),最通用;
  7. 文件

选择依据:速度、跨机器、数据量、同步需求。前端联系:浏览器多进程、Node IPC、Worker postMessage(消息 vs 共享内存)。


114. 进程调度策略

  1. FCFS:先来先服务,护航效应
  2. SJF/SRTF:平均等待最短,长任务可能饥饿
  3. 时间片轮转 RR:公平、交互好,时间片需权衡;
  4. 优先级调度:可能饥饿(用老化解决);
  5. 多级反馈队列 MLFQ:多队列 + 动态升降,现代 OS 主流

考点:抢占/非抢占;MLFQ 为何自适应 IO 密集进程。


115. 死锁

死锁四必要条件:

  1. 互斥;2. 占有且等待;3. 不可剥夺;4. 循环等待

处理

  • 预防:破坏四条件之一(一次性分配、按序申请);
  • 避免银行家算法(判断安全状态);
  • 检测恢复:检测等待图有环 → 撤销/剥夺;
  • 鸵鸟策略(概率低就重启)。

考点:背四条件 + 各举一例;预防 vs 避免 vs 检测;死锁 vs 活锁。


116. IO 多路复用

一个线程同时监听多个 fd,内核通知就绪再处理——高并发网络的基础。

演进

  1. select:轮询 O(n),fd 上限 1024,拷贝集合;
  2. poll:链表存 fd 无上限,仍 O(n);
  3. epoll(Linux)事件驱动,红黑树 + 就绪链表,只返回就绪 fd(O(1));支持 LT/ET;无需反复拷贝。

macOS 用 kqueue;Windows 用 IOCP。

与前端联系Node 的 libuv 在 Linux 用 epoll → 单线程 + 非阻塞 + epoll = 高并发模型;Nginx 同理。

考点:三模型对比表;"Node 单线程凭什么高并发";ET/LT 区别。


十、JS 手写题(含 Vue keep-alive)

说明:本章除第 117 题(Vue keep-alive)外,118–127 均为通用 JS 手写题,与 Vue 无关——按原题表顺序收录,特此标注以免误判归属。

117. Vue keep-alive

<keep-alive> 缓存动态组件/路由组件实例,避免重复销毁重建(保留状态与 DOM)。

  • <keep-alive include="A,B" max="10"><router-view/></keep-alive>
  • 新增钩子:activated(重新激活)与 deactivated(被缓存停用);
  • 原理:组件不销毁,移入隐藏容器缓存,命中直接复用;
  • 缓存过多用 max(LRU 淘汰)。
<keep-alive :include="['List']" :max="10">
  <router-view />
</keep-alive>

118. 实现防抖函数(debounce)

触发后延迟执行,期间再触发重置计时——搜索联想、resize:

function debounce(fn, wait = 300, immediate = false) {
  let timer = null;
  return function (...args) {
    const callNow = immediate && !timer;
    clearTimeout(timer);
    timer = setTimeout(() => {
      timer = null;
      if (!immediate) fn.apply(this, args);
    }, wait);
    if (callNow) fn.apply(this, args);
  };
}

要点:保留 this;可加 cancel 方法。


119. 实现节流函数(throttle)

固定频率执行——滚动加载、拖拽、连点:

// 时间戳版
function throttle(fn, wait = 300) {
  let last = 0;
  return function (...args) {
    const now = Date.now();
    if (now - last >= wait) { last = now; fn.apply(this, args); }
  };
}
// 定时器版(尾部补一次)
function throttleTimer(fn, wait) {
  let timer = null;
  return function (...args) {
    if (!timer) timer = setTimeout(() => { timer = null; fn.apply(this, args); }, wait);
  };
}

防抖 vs 节流:防抖="停下才算完",节流="按时执行限频"。


120. 实现字符串翻转

const rev = s => [...s].reverse().join('');     // 正确处理 Unicode 码点
// 双指针
function reverseStr(s) {
  const arr = [...s];
  let i = 0, j = arr.length - 1;
  while (i < j) { [arr[i], arr[j]] = [arr[j], arr[i]]; i++; j--; }
  return arr.join('');
}

考察点split('') 会拆坏 emoji/代理对 → 用 [...s];进阶"按单词翻转"。


121. 数组去重

const uniq = arr => [...new Set(arr)];                     // 基本类型
const uniqBy = (arr, key) => [...new Map(arr.map(o => [key(o), o])).values()];  // 对象

考察点:对象数组去重需 Map + 唯一 key;NaN(Set 能去重,indexOf 方案不行)。


122. 实现千位分隔符

function format(n) {
  const [int, dec = ''] = String(n).split('.');
  const withComma = int.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  return dec ? `${withComma}.${dec}` : withComma;
}
format(1234567.891);   // '1,234,567.891'
// 原生: (1234567.891).toLocaleString('en-US')

考察点:正则 \B(?=(\d{3})+(?!\d)) 原理;兼容小数。


123. 判断是否是回文数

const isPalindrome = x => {
  const s = String(x);
  return s === [...s].reverse().join('');
};
// 数值版(反转一半防溢出)
function isPalindromeNum(x) {
  if (x < 0 || (x % 10 === 0 && x !== 0)) return false;
  let rev = 0;
  while (x > rev) { rev = rev * 10 + (x % 10); x = Math.floor(x / 10); }
  return x === rev || x === Math.floor(rev / 10);
}

124. 实现一个模板引擎

function render(tpl, data) {
  return tpl.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, key) =>
    key.split('.').reduce((o, k) => (o == null ? o : o[k]), data) ?? ''
  );
}
render('你好 {{ user.name }}', { user: { name: '张三' } });  // '你好 张三'

考点:正则 + reduce 深层读取;进阶 with + new Function 的原理与 XSS 风险;联想 Vue 模板编译生成 render 函数。


125. 判断一个数是否是素数

function isPrime(n) {
  if (n < 2) return false;
  if (n <= 3) return true;
  if (n % 2 === 0 || n % 3 === 0) return false;
  for (let i = 5; i * i <= n; i += 6)
    if (n % i === 0 || n % (i + 2) === 0) return false;
  return true;
}

考察点:边界(0/1/2);只需试到 √n;6k±1 步进。


126. 翻转二叉树

function invertTree(root) {
  if (!root) return null;
  [root.left, root.right] = [root.right, root.left];
  invertTree(root.left);
  invertTree(root.right);
  return root;
}
// 迭代 BFS 同样逐个交换

考点:递归三要素;警惕交换后用原引用递归的坑。


127. 获取 n 以内所有素数(埃氏筛)

function primesBelow(n) {
  const isPrime = new Array(n + 1).fill(true);
  isPrime[0] = isPrime[1] = false;
  for (let i = 2; i * i <= n; i++)
    if (isPrime[i])
      for (let j = i * i; j <= n; j += i) isPrime[j] = false;   // 从 i² 开始标记
  return isPrime.map((v, i) => (v ? i : -1)).filter(i => i >= 0);
}

要点:从 i*i 开始(更小倍数已被标);O(n log log n);超大 n 用分段筛。


十一、综合收尾(HTTP / JS 继承 / 浏览器工作原理)

说明:第 129 题(JS 继承)属语言基础,第 128 题(HTTP)属网络,第 130 题(浏览器工作原理)是全局总览题——三题主题不同,按原表顺序收在本章。复习建议:第 130 题请与第三章 C 组的"第 45 题 URL 到页面"对照看,两题是同一渲染流程的"细讲版 vs 总览版"。

128. HTTP

HTTP:应用层协议,请求-响应、无状态。

结构:请求行(method + URL + 版本)+ headers + body;响应 = 状态行 + headers + body。
方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS;GET/PUT/DELETE 幂等。

状态码:2xx(200/201/204);3xx(301 永久、302 临时、304 缓存命中);4xx(400/401/403/404/405/429);5xx(500/502/503/504)。

特性:无状态(Cookie/Token 维持);演进(队头阻塞 → HTTP/2/3)。

考点:301/302/304 区分;GET vs POST(语义/幂等/缓存);常见请求头(Cache-Control、CORS 的 Origin 等)。


129. 继承(JS)

继承实现方式演进:

  1. 原型链继承:子原型指向父实例——引用类型共享问题;
  2. 构造函数继承:父.call(this)——方法无法复用;
  3. 组合继承:两者结合,父构造调用两次;
  4. 寄生组合继承(标准)
function inherit(Child, Parent) {
  Child.prototype = Object.create(Parent.prototype);
  Child.prototype.constructor = Child;
}
function Parent(name) { this.name = name; }
Parent.prototype.say = function () { return this.name; };
function Child(name, age) { Parent.call(this, name); this.age = age; }
inherit(Child, Parent);
const c = new Child('tom', 8);
c instanceof Parent; // true
  1. ES6 class extends:语法糖(寄生组合 + super 顺序要求)。

考点:每种方案解决什么/引入什么;Object.create 作用;extends 的 proto 两条线。


130. 浏览器工作原理(总览)

从 HTML 到像素的完整链路:

  1. 导航:URL → DNS → TCP/TLS → 请求 HTML;
  2. 解析 HTML → DOM 树;解析 CSS → CSSOM
  3. 合并 Render Tree(剔除 display:none 等不可见);
  4. Layout:计算几何;Paint:生成绘制指令;
  5. 合成 Composite:分层 → 光栅化 → GPU 合成显示;
  6. 脚本时机:JS 阻塞解析(defer 延后、async 即下即执);CSS 阻塞渲染。

现代渲染优化

  • 关键渲染路径(CRP)优化首屏;
  • 流式解析边下边渲染;
  • 渲染进程多线程:主线程(JS/layout/paint)、合成线程(滚动合成)。

考点:按"解析→渲染树→布局→绘制→合成"完整复述,说明哪些阶段可被 JS 影响、如何优化。


以上 130 题答案整理自《前端面试真题,会80%直接进大厂》多维表格中的面试知识点,按原表顺序逐题给出详解。
原表「知识点(点击链接学习)」列保留了每题的参考学习链接,建议配合原文档对照使用。