前言:
2026 年的春招可以用一个词形容: “卷中卷” 。单纯会写 Vue/React 业务代码已经很难过简历关了,面试官现在更看重你的底层原理、工程化基建(如 Rspack/Vite/微前端)、性能优化以及复杂场景的解决能力。
笔者双非本,三年中小厂前端经验,经过一个多月的地狱级复习,最终拿下了字节跳动、淘天集团(阿里)、美团的三家 Offer。今天把这一个月的面经和高频手写题全部复盘出来,希望给正在求职的兄弟们一点参考! (文末附高频手撕代码题,建议收藏反复手敲!)
一、 字节跳动(抖音电商团队)
面试特点: 极其看重计算机基础、算法能力和源码理解。基本每一轮都会有一到两道 Hard/Medium 级别的算法题或手写题。
一面(基础与深度,约 60 分钟)
一面面试官主要考察基础的扎实程度,问得很细。
二面(项目与性能,约 70 分钟)
二面压力剧增,面试官会顺着你的项目往深处挖。
三面(主管面,约 45 分钟)
主管面主要考察业务思考、抗压能力和对前沿技术的敏感度。
二、 淘天集团(阿里,大淘宝技术)
面试特点: 偏向前端工程化、复杂架构设计(微前端/Monorepo)以及对业务指标的理解。
一面(技术广度,约 50 分钟)
二面(架构与基建,约 60 分钟)
三、 美团(到店事业群)
面试特点: 极其注重原生 JS 功底,有很多场景题,喜欢问跨端(小程序/H5)。
一面(JS 核心,约 50 分钟)
二面(场景与安全,约 60 分钟)
四、 🏆 高频手写题复盘(必背代码区)
面试前,这几段代码请务必在白纸上能默写出来!
1. 带并发限制的异步调度器(字节高频)
题目描述: 实现一个 Scheduler,支持并发执行异步任务,但最大并发数受限。
code JavaScript
downloadcontent_copy
expand_less
class Scheduler {
constructor(limit) {
this.limit = limit; // 最大并发数
this.count = 0; // 当前正在运行的任务数
this.queue =[]; // 等待队列
}
add(task) {
return new Promise((resolve) => {
task.resolve = resolve;
if (this.count < this.limit) {
this.run(task);
} else {
this.queue.push(task);
}
});
}
run(task) {
this.count++;
task().then((res) => {
task.resolve(res);
this.count–;
// 执行完一个,如果队列有任务,继续执行
if (this.queue.length > 0) {
this.run(this.queue.shift());
}
});
}
}
// 测试代码
const scheduler = new Scheduler(2);
const timeout = (time) => new Promise(r => setTimeout(r, time));
const addTask = (time, order) => {
scheduler.add(() => timeout(time)).then(() => console.log(order));
};
addTask(1000, '1');
addTask(500, '2');
addTask(300, '3');
addTask(400, '4');
// 输出顺序: 2, 3, 1, 4
2. 完美的深拷贝(阿里高频)
题目描述: 实现深拷贝,需处理数组、对象、Map、Set 以及循环引用。
code JavaScript
downloadcontent_copy
expand_less
function deepClone(obj, hash = new WeakMap()) {
// 处理 null 或非对象
if (obj === null || typeof obj !== 'object') return obj;
// 处理 Date 和 RegExp
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 处理循环引用,如果 hash 中有,直接返回
if (hash.has(obj)) return hash.get(obj);
// 处理 Map
if (obj instanceof Map) {
const map = new Map();
hash.set(obj, map);
obj.forEach((val, key) => {
map.set(key, deepClone(val, hash));
});
return map;
}
// 处理 Set
if (obj instanceof Set) {
const set = new Set();
hash.set(obj, set);
obj.forEach(val => {
set.add(deepClone(val, hash));
});
return set;
}
// 处理数组和普通对象
const cloneObj = new obj.constructor();
hash.set(obj, cloneObj);
// 使用 Reflect.ownKeys 获取所有键,包括 Symbol
Reflect.ownKeys(obj).forEach(key => {
cloneObj[key] = deepClone(obj[key], hash);
});
return cloneObj;
}
3. 一维数组转树形结构(美团高频)
题目描述: 将带有 id 和 parentId 的扁平化数组,转换为树形结构。
code JavaScript
downloadcontent_copy
expand_less
const list =[
{ id: 1, name: '部门A', parentId: 0 },
{ id: 2, name: '部门B', parentId: 0 },
{ id: 3, name: '部门C', parentId: 1 },
{ id: 4, name: '部门D', parentId: 1 },
{ id: 5, name: '部门E', parentId: 2 },
];
function listToTree(data) {
const map = {};
const tree =[];
// 先把数据映射进 map
data.forEach(item => {
map[item.id] = { …item, children:[] };
});
// 再遍历一遍,寻找父节点
data.forEach(item => {
if (item.parentId === 0) {
// 根节点
tree.push(map[item.id]);
} else {
// 找到父节点,塞进 children
if (map[item.parentId]) {
map[item.parentId].children.push(map[item.id]);
}
}
});
return tree;
}
console.log(JSON.stringify(listToTree(list), null, 2));
拿大厂 Offer,三分靠运气,七分靠实力。对于三年经验的前端来说,**“差异化竞争”**是关键:
祝各位前端兄弟在这个金三银四顺利上岸,拿到满意的薪资!
如果这篇文章对你有帮助,欢迎【点赞 + 收藏 + 关注】!有任何面试问题,欢迎在评论区留言交流,我每天都会看!

