前言
大家好,我是木斯佳。
在这个春节假期,当大家都在谈论返乡、团圆与休息时,作为一名技术人,我的思考却不由自主地转向了行业的「冬」与「春」。
相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的"增删改查"岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。
正值春节,也是复盘与规划的好时机。结合CSDN这次「春节代码贺新年」活动所提倡的"用技术视角记录春节、复盘成长",我决定在这个假期持续更新专栏,帮助年后参加春招的同学。
这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。
我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。
温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。
在这个假期,让我们一起充电,为下一个技术春天做好准备。

面经原文内容
📍面试公司:腾讯wxg技术架构
🕐面试时间:2025.11.19
💻面试岗位:前端
❓面试问题:
-
1.问c和c++,问js的内存管理,说了新生代和老年代blabla
问是否了解其他语言的内存管理机制
-
2.如何实现全双工通信
websocket具体是如何实现全双工通信的?
-
3.css布局常见的有哪几种方式,然后问flex常见的属性
我说:flex布局、grid、绝对定位
-
4.问操作系统
进程和线程的定义及区别 ,是否了解协程
CPU和GPU的区别, CPU是什么,CPU和GPU擅长处理什么任务
举例子什么任务适合用GPU
从写代码到程序运行,中间经过了哪些环节
问静态链接是什么
-
5.问AI Coding
项目中AI的使用情况
中间省略一些和项目有关的问题
代码题
让我下载文件和lru有关的,问我知不知道LRU原理,我说不太清楚
给我换了个字符串有关的题,难度降低了很多,简单题但是没怎么刷过写了很久
🙌面试感想:
因为不是纯前端的岗位,除了前端的知识问了很多网络、系统、编译原理的。这方面的回答不太好建议我多刷算法题,夯实基础。
来源: 牛客网 许愿给我个offer
📝 腾讯WXG技术架构前端面经·深度解析
🎯 面试整体画像
| 维度 | 特征 |
|---|---|
| 部门定位 | 微信事业群(WXG)- 技术架构部 |
| 面试风格 | 全栈式 + 底层原理型 + 广度覆盖型 |
| 难度评级 | ⭐⭐⭐⭐⭐(五星,跨领域深度) |
| 考察重心 | 内存管理、网络通信、操作系统、编译原理、AI Coding |
🧠 JS内存管理·V8垃圾回收机制
问题2:JS内存管理(新生代、老年代)
✅ 深度解析:
1. V8内存结构
javascript
// V8进程内存限制(64位:1.4GB,32位:0.7GB)
// 主要分为:
- 新生代(New Space):1-8MB,存活时间短的对象
- 老年代(Old Space):大部分内存,存活时间长的对象
- 大对象空间(Large Object Space):>1MB的对象
- 代码空间(Code Space):JIT编译后的代码
- Map空间(Map Space):隐藏类
2. 新生代回收(Scavenge算法)
javascript
// 采用Cheney算法(复制算法)
// 新生代分为两个半区:From空间 和 To空间
流程:
1. 新对象分配在From空间
2. From空间快满时,触发垃圾回收
3. 标记From空间中的存活对象
4. 将存活对象复制到To空间
5. 清理From空间
6. 交换From和To角色
// 对象晋升条件:
- 经历过一次Scavenge仍然存活
- To空间使用率超过25%
3. 老年代回收(标记-清除-整理)
javascript
// 标记阶段
从根对象(全局对象、栈变量)出发,标记所有可达对象
// 清除阶段
清除未被标记的对象,产生内存碎片
// 整理阶段(可选)
将存活对象移动到连续内存,解决碎片问题
4. 增量标记与延迟清理
javascript
// 避免全停顿(Stop-The-World)
- 增量标记:分多次执行标记,每次5-10ms
- 延迟清理:按需清理,而不是一次性清理完
- 并发标记:辅助线程并行标记
5. 内存泄漏场景
javascript
// 常见内存泄漏:
1. 意外的全局变量
function foo() {
bar = '全局变量'; // 没有声明,挂在window上
}
2. 闭包引用
function createClosure() {
const largeData = new Array(1000000);
return function() {
console.log('closure');
// largeData一直被引用,无法回收
};
}
3. 定时器未清理
setInterval(() => {
// 如果组件卸载时没有clearInterval,一直执行
}, 1000);
4. DOM引用
const elements = [];
document.querySelectorAll('div').forEach(el => {
elements.push(el); // DOM移除后,引用还在
});
追问:是否了解其他语言的内存管理机制?
| 语言 | 内存管理方式 | 特点 |
|---|---|---|
| C/C++ | 手动管理(malloc/free) | 灵活但易错 |
| Java | JVM垃圾回收(分代收集) | 自动但不可控 |
| Python | 引用计数 + 分代回收 | 循环引用问题 |
| Rust | 所有权系统 + 借用检查 | 编译期确定,无运行时开销 |
| Go | 并发三色标记 | 低延迟 |
💡 面试官考察点:
不是要你背V8源码,而是看你对程序运行时的理解深度
知道内存分代,说明你理解"不同对象生命周期不同"
能对比其他语言,说明你有横向思考能力
🔄 全双工通信·WebSocket深度解析
问题3:如何实现全双工通信?WebSocket具体是如何实现的?
✅ 完整答案:
1. 全双工通信方案对比
| 方案 | 方向 | 实现方式 | 适用场景 |
|---|---|---|---|
| WebSocket | 双向 | 升级HTTP协议 | 实时应用、游戏 |
| SSE | 单向(服务端→客户端) | 纯HTTP | 推送通知 |
| WebRTC | 双向 | P2P + 信令 | 音视频通话 |
| 长轮询 | 模拟双向 | 不断发送HTTP请求 | 兼容老浏览器 |
2. WebSocket握手过程
javascript
// 客户端请求
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket // 告诉服务器要升级协议
Connection: Upgrade // 连接升级
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== // 随机key
Sec-WebSocket-Version: 13 // 协议版本
// 服务端响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= // key+魔数sha1
3. WebSocket帧结构(二进制协议)
javascript
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| |Masking-key, if MASK set to 1 |
+-------------------------------+-------------------------------+
| Masking-key (continued) | Payload Data |
+-------------------------------- - - - - - - - - - - - - - - - +
: Payload Data continued ... :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| Payload Data continued ... |
+---------------------------------------------------------------+
4. 关键机制
javascript
// 掩码(Masking)
- 客户端发送的数据必须掩码(防止缓存污染攻击)
- 服务端发送的数据不需要掩码
// 心跳(Ping/Pong)
- 保持连接活跃
- 检测连接状态
// 分片(Fragmentation)
- 大消息分成多个帧发送
- FIN位表示最后一帧
5. 前端实现
javascript
// 基础实现
const ws = new WebSocket('wss://example.com/chat');
ws.onopen = () => {
console.log('连接建立');
ws.send('Hello Server!');
};
ws.onmessage = (e) => {
console.log('收到消息:', e.data);
};
ws.onclose = () => {
console.log('连接关闭');
};
ws.onerror = (error) => {
console.error('WebSocket错误:', error);
};
// 心跳保持
const heartbeat = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send('ping');
}
}, 30000);
// 二进制数据
ws.binaryType = 'arraybuffer';
ws.send(new Uint8Array([1, 2, 3]).buffer);
💡 面试官考察点:
知道WebSocket怎么用是基础
知道握手过程、帧结构、掩码机制,才说明真的理解协议
🎨 CSS布局·从使用到原理
问题4:CSS布局常见方式 + Flex属性
✅ 完整总结:
1. 布局方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 普通流 | 文档流布局 | 简单 | 无法精细控制 |
| 浮动 | 文字环绕 | 兼容性好 | 需要清除浮动 |
| 定位 | 叠加、固定元素 | 精确控制 | 脱离文档流 |
| Flex | 一维布局 | 灵活、强大 | 二维控制弱 |
| Grid | 二维布局 | 最强大 | 学习成本高 |
| 多列 | 瀑布流 | 简单 | 控制有限 |
2. Flex完整属性(面试官想听的)
css
/* 容器属性 */
.container {
display: flex;
/* 主轴方向 */
flex-direction: row | row-reverse | column | column-reverse;
/* 换行 */
flex-wrap: nowrap | wrap | wrap-reverse;
/* 主轴对齐 */
justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
/* 交叉轴对齐 */
align-items: stretch | flex-start | flex-end | center | baseline;
/* 多行交叉轴对齐 */
align-content: flex-start | flex-end | center | space-between | space-around | stretch;
/* 行间距 */
gap: 10px 20px; /* row-gap column-gap */
}
/* 项目属性 */
.item {
/* 排列顺序,越小越靠前 */
order: 0;
/* 放大比例,默认为0 */
flex-grow: 0;
/* 缩小比例,默认为1 */
flex-shrink: 1;
/* 初始大小 */
flex-basis: auto;
/* 简写 */
flex: 0 1 auto;
/* 单独对齐 */
align-self: auto | flex-start | flex-end | center | baseline | stretch;
}
3. Flex计算规则
javascript
// 剩余空间分配
容器宽度 = 600px
三个项目flex-basis = 100px
剩余空间 = 600 - 300 = 300px
// 如果flex-grow: 1, 2, 1
总份数 = 4
每份 = 300 / 4 = 75px
项目1: 100 + 75*1 = 175px
项目2: 100 + 75*2 = 250px
项目3: 100 + 75*1 = 175px
🖥️ 操作系统·进程线程协程
问题5:进程和线程的定义及区别,是否了解协程
✅ 深度解析:
1. 进程 vs 线程
| 维度 | 进程 | 线程 |
|---|---|---|
| 定义 | 资源分配最小单位 | CPU调度最小单位 |
| 内存 | 独立地址空间 | 共享进程内存 |
| 开销 | 创建销毁开销大 | 轻量,开销小 |
| 通信 | IPC(管道、消息队列) | 直接读写共享内存 |
| 稳定性 | 一个进程崩溃不影响其他 | 一个线程崩溃可能导致进程崩溃 |
| 切换 | 切换慢(需要切换页表) | 切换快(寄存器切换) |
2. 协程(Coroutine)
javascript
// 定义:用户态线程,协作式调度
特点:
- 一个线程可以多个协程
- 由程序自己控制切换(非抢占式)
- 切换开销极小(≈函数调用)
- 没有并发安全问题
// JS中的协程(Generator)
function* coroutine() {
console.log('开始');
yield 1; // 让出控制权
console.log('继续');
yield 2;
console.log('结束');
}
const gen = coroutine();
gen.next(); // 输出"开始",返回{value: 1}
gen.next(); // 输出"继续",返回{value: 2}
gen.next(); // 输出"结束"
// async/await 也是协程
async function task() {
console.log('任务开始');
await sleep(1000); // 让出控制权
console.log('任务继续');
}
3. 三者的关系
一个进程可以包含多个线程
一个线程可以包含多个协程
调度层次:
- 进程:操作系统调度(内核态)
- 线程:操作系统调度(内核态)
- 协程:程序自己调度(用户态)
🔧 CPU vs GPU·深度对比
问题6:CPU和GPU的区别,各自擅长什么
✅ 完整答案:
1. 设计哲学对比
| 维度 | CPU | GPU |
|---|---|---|
| 设计目标 | 低延迟 | 高吞吐量 |
| 核心数量 | 少(4-16核) | 极多(数千核) |
| 核心功能 | 强控制单元、大缓存 | 简单计算单元、小缓存 |
| 擅长任务 | 串行计算、复杂逻辑 | 并行计算、简单运算 |
| 比喻 | 几个博士 | 数千小学生 |
2. 架构对比
CPU:
[控制单元][缓存]
[ 核心1 ][ 核心2 ]
[ 核心3 ][ 核心4 ]
GPU:
[核心1][核心2][核心3][核心4]...[核心N]
[核心1][核心2][核心3][核心4]...[核心N]
[核心1][核心2][核心3][核心4]...[核心N]
3. GPU适合的任务
javascript
// 1. 图形渲染
- 顶点着色
- 像素着色
- 纹理映射
// 2. 矩阵运算
const A = [[1,2,3], [4,5,6], [7,8,9]];
const B = [[9,8,7], [6,5,4], [3,2,1]];
// 矩阵乘法:每个元素独立计算 → 高度并行
// 3. 深度学习
- 卷积运算
- 批量矩阵乘法
- 激活函数计算
// 4. 图像处理
- 滤镜(每个像素独立计算)
- 图像缩放
- 特征提取
// 5. 物理模拟
- 粒子系统
- 流体力学
- 碰撞检测
4. 前端中的GPU
javascript
// CSS 3D Transform
transform: translate3d(100px, 100px, 0);
// 触发GPU加速,独立合成层
// Canvas
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl');
// 使用GPU渲染
// WebGPU(下一代)
// 更底层访问GPU,适合计算任务
📦 从代码到运行·编译链接全过程
问题7:从写代码到程序运行,中间经过了哪些环节
✅ 完整流程:
1. 预处理(Preprocessing)
c
// 源代码(main.c)
#include <stdio.h>
#define PI 3.14
int main() {
printf("PI = %f\n", PI);
return 0;
}
// 预处理后
// 展开头文件、宏替换
int main() {
printf("PI = %f\n", 3.14);
return 0;
}
2. 编译(Compilation)
源代码 → 词法分析 → 语法分析 → 语义分析 →
中间代码生成 → 优化 → 目标代码生成 → 汇编代码
词法分析:识别关键字、标识符、运算符
语法分析:构建抽象语法树(AST)
语义分析:类型检查、作用域检查
优化:常量折叠、死代码消除
3. 汇编(Assembly)
汇编代码 → 机器指令(目标文件 .o/.obj)
生成符号表:
- 定义的函数、全局变量
- 引用的外部符号
4. 链接(Linking)
javascript
// 静态链接
将所有用到的库代码复制到可执行文件
优点:独立运行
缺点:体积大、更新库需重新链接
// 动态链接
运行时加载共享库(.dll/.so)
优点:体积小、共享内存
缺点:依赖环境
// 链接过程
1. 符号解析:每个符号对应一个地址
2. 重定位:修改代码中的地址引用
3. 合并段:合并相同属性的段(.text, .data)
5. 加载执行
可执行文件 → 加载器 → 内存 → 进程
操作系统做:
- 分配内存空间
- 建立虚拟地址映射
- 将代码和数据加载到内存
- 跳转到入口点(main函数)
6. 前端特例(JS)
javascript
// JS是解释执行 + JIT编译
写代码 → 解析 → AST → 字节码 → 执行
↓
热点代码
↓
JIT编译为机器码
↓
直接执行
🔗 静态链接·深度解析
问题8:静态链接是什么?
✅ 完整答案:
1. 定义
静态链接是在编译时将程序所需的所有库代码复制到可执行文件中的过程
2. 链接过程
c
// a.c
extern int global_var;
void func();
int main() {
func();
return 0;
}
// b.c
int global_var = 42;
void func() {
global_var++;
}
// 静态链接后
可执行文件包含:
- main函数的代码
- func函数的代码
- global_var变量
- 所有依赖都已解析
3. 静态链接 vs 动态链接
| 维度 | 静态链接 | 动态链接 |
|---|---|---|
| 时机 | 编译时 | 运行时 |
| 文件大小 | 大 | 小 |
| 启动速度 | 快 | 慢(需加载库) |
| 内存使用 | 每个进程一份 | 共享一份 |
| 更新 | 需重新链接 | 替换库文件 |
| 依赖 | 无 | 需要库存在 |
4. 优缺点分析
javascript
// 优点
- 独立运行,不依赖环境
- 加载速度快
- 版本确定,不会冲突
// 缺点
- 文件体积大
- 内存浪费(多个进程用同一库)
- 更新库需要重新编译
5. 前端中的"链接"
javascript
// Webpack打包就像静态链接
// 将所有依赖打包到一个bundle.js
// 代码分割就像动态链接
import('lodash').then(_ => {
// 运行时加载
});
// 优缺点完全对应
- 打包体积大但独立运行
- 代码分割体积小但依赖网络
🤖 AI Coding·项目中的应用
问题9:项目中AI的使用情况
✅ 总结框架:
1. AI作为开发助手
javascript
// 代码生成
- 用Copilot写重复代码
- 生成单元测试
- 写注释和文档
// Debug辅助
- 解释错误信息
- 建议修复方案
// 重构建议
- 识别代码坏味道
- 提出优化方案
2. AI作为产品功能
javascript
// 智能搜索
- 语义理解用户查询
- 返回相关结果
// 代码辅助
- 根据描述生成代码片段
- 代码解释
// 对话式交互
- 自然语言操作界面
- 智能客服
3. 实践案例
javascript
// 项目1:AI代码助手
- 用户输入需求
- 调用大模型生成代码
- 展示并允许编辑
// 项目2:智能文档
- 上传文档
- 自动提取关键信息
- 生成摘要和标签
4. 技术挑战
javascript
// 性能问题
- 流式输出保证体验
- 加载状态管理
// 错误处理
- 模型输出不稳定
- 兜底方案
// 成本控制
- 缓存常见请求
- 降级方案
📝 代码题·LRU与字符串
问题10:LRU原理 + 字符串题
✅ LRU原理(如果知道的话)
javascript
// LRU = Least Recently Used 最近最少使用
// 核心思想:
- 缓存有容量限制
- 新访问的数据放到最前面
- 容量满时,删除最久未访问的数据
// 实现方式:
- Map(维护插入顺序)
- 双向链表 + 哈希表
// 前端应用场景:
- 浏览器缓存
- 图片懒加载
- 路由缓存
候选人回答"不太清楚" → 扣分点
✅ 字符串题(简单但易错)
javascript
// 假设题目:反转字符串中的单词
// 输入:"the sky is blue"
// 输出:"blue is sky the"
function reverseWords(s) {
// 1. 去除多余空格
// 2. 分割单词
// 3. 反转数组
// 4. 重新组合
return s.trim()
.split(/\s+/)
.reverse()
.join(' ');
}
// 注意点:
- 多个空格处理
- 首尾空格处理
- 原地反转(进阶)
💡 面经关键点:
"简单题但没怎么刷过写了很久" → 算法是硬伤,必须补
WXG这种部门,算法题做不出来基本就挂了
🎁 附:WXG技术架构面试复习清单
| 知识点 | 掌握程度 | 重点方向 |
|---|---|---|
| JS内存管理 | ⭐⭐⭐⭐⭐ | V8分代、垃圾回收算法、内存泄漏 |
| 其他语言内存 | ⭐⭐⭐ | Java/Python/Rust对比 |
| WebSocket | ⭐⭐⭐⭐⭐ | 握手、帧结构、掩码、心跳 |
| CSS布局 | ⭐⭐⭐ | Flex完整属性、Grid |
| 进程线程协程 | ⭐⭐⭐⭐ | 定义、区别、协程原理 |
| CPU/GPU | ⭐⭐⭐ | 架构对比、GPU任务 |
| 编译链接 | ⭐⭐⭐⭐ | 预处理→编译→汇编→链接 |
| 静态链接 | ⭐⭐⭐ | 原理、与动态链接对比 |
| LRU缓存 | ⭐⭐⭐⭐⭐ | 原理、实现、应用场景 |
| 字符串处理 | ⭐⭐⭐ | 常见题型、边界处理 |
📌 最后一句:
WXG技术架构的面试,是一场计算机科学综合大考 。
他们要的不是"会用框架的人",
他们要的是能理解程序运行本质的人 。
当面试官问到"静态链接是什么"的时候,他其实在问:
"你真的理解你的代码是怎么跑起来的吗?"