尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

2024字节前端笔试复盘:核心考点与高频算法题全解析

2024字节前端笔试复盘:核心考点与高频算法题全解析 2024秋招是个很有意思的节点前端岗的竞争比前几年更白热化但字节的笔试风格其实一直挺稳定——不玩花活重点就考察你的计算机基础扎不扎实、代码能不能在高压环境下快速写对。我花了一个周末把所有能找到的2024秋招前端笔试题和面经回忆版过了一遍今天整理成这篇完整的复盘笔记从题型分布到专项考点再到具体的算法题解法和前端高频手写题一次讲透。不管你是准备明年春招还是打算冲刺2026届秋招这篇都可以当作战前手册用。1. 整体题型与考察逻辑拆解1.1 笔试整体结构字节前端笔试一般是在牛客网或者自家的招聘平台上完成时长通常在90分钟到120分钟之间。2024秋招的题型结构大体是三类选择题单选多选约15-25道覆盖JavaScript基础、浏览器原理、CSS布局、网络协议、前端框架React/Vue、工程化等。编程题2-3道以LeetCode中档题为主偶尔会出现hard题的简化版。重点考察数组、字符串、链表、二叉树、动态规划、双指针、贪心这些大类。主观题/设计题不一定有近两年偶尔会出现“设计一个xxx组件”或“描述从输入URL到页面渲染的完整过程”这类开放题考察表达能力和系统思维。我的建议是做选择题时不要恋战一道题如果纠结超过90秒先标记跳过去。编程题才是分水岭选择题大家差距拉不开编程题一题之差可能就是面试门槛的区别。1.2 考察逻辑与选人标准字节的前端笔试其实透露出一个很明确的信号他们不指望你笔试阶段就会写业务代码而是希望看到你的编程基本功和解决问题的思维路径。前端技术栈迭代很快但数据结构与算法、网络基础、浏览器渲染原理这些东西是十年都不会变的。从笔试筛人逻辑来看编程题ACAccept一道是及格线AC两道基本能稳进面试AC三道就是高分选手。选择题错5道以内问题不大但如果编程题挂零那选择题基本全对也可能被刷掉。2. 核心考点逐个击破2.1 JavaScript与语言基础前端笔试里JavaScript永远是最核心的板块字节的选择题几乎必考以下几类变量提升与暂时性死区this指向问题特别是箭头函数与普通函数的区别闭包与作用域链原型链与继承Promise执行顺序微任务与宏任务Promise的顺序问题是高频中的高频笔试里几乎年年出现。我给你一个快速判断的方法先找同步代码再找微任务Promise.then、queueMicrotask最后找宏任务setTimeout、setInterval。同一类型的任务按代码出现的顺序执行。下面这道题是2024秋招多个前端岗位出现过的原题变形console.log(start); setTimeout(() { console.log(timeout1); }, 0); Promise.resolve().then(() { console.log(promise1); }).then(() { console.log(promise2); }); console.log(end);执行结果依次是start、end、promise1、promise2、timeout1。每次遇到这种题我都建议大家先自己写一遍再跑控制台验证多试几个嵌套变体。面试官特别喜欢在这个基础上增加async/await的组合你要记住async函数内部的await行相当于把后续代码塞进了微任务队列。2.2 CSS布局与浏览器渲染CSS部分选择题一般占总题量的20%左右重点集中在flex布局的各类属性flex-grow、flex-shrink、flex-basis的优先级关系grid布局比较少但偶尔考浏览器渲染机制回流、重绘、合成盒模型标准盒模型与IE盒模型flex布局是必考的。很多人只知道flex: 1代表放大但不清楚flex: 1实际上是flex-grow: 1、flex-shrink: 1、flex-basis: 0%的缩写。笔试题里经常让你数三个子项在不同flex属性下各占多少宽度如果不清楚flex的缩写规则和优先级很容易算错。字节笔试还特别喜欢考回流reflow和重绘repaint的区别以及哪些操作会触发回流。记住一个核心原则凡是需要重新计算几何属性尺寸、位置的操作必定引发回流比如修改宽度、读写offsetHeight、clientWidth等。只改变颜色、阴影、背景这类不影响布局的属性则只触发重绘。还有一个冷门考点是transform为什么比修改left/top更高效原因在于transform只触发合成composite不会引起回流和重绘整个过程可以由GPU完成。2.3 浏览器与网络协议网络这块主要集中在HTTP缓存、跨域解决方案、HTTPS握手过程、TCP三次握手和四次挥手。前端岗位笔试对网络的考察不会像后端那么深但基本概念必须清楚。特别是HTTP缓存选择题很喜欢给出一个场景让你判断该走强缓存还是协商缓存。强缓存对应的响应头是Cache-Control和Expires协商缓存对应的是Last-Modified/If-Modified-Since和ETag/If-None-Match。一个判断优先级的小技巧Cache-Control中max-age和s-maxage同时存在时s-maxage生效针对CDN。no-cache是每次都要回源验证no-store才是完全不用缓存。跨域问题也是高频考点尤其是JSONP和CORS的适用场景。2024秋招出现了好几道关于CORS预检请求的题你要知道什么情况下会触发OPTIONS预检非简单请求例如自定义请求头、Content-Type非application/x-www-form-urlencoded等。只要记住预检是浏览器自动发起的后端需要正确处理OPTIONS请求即可。2.4 框架与工程化框架考察以React为主、Vue为辅字节内部主推React所以笔试选择题中React相关的题多于Vue。常考的点包括React Hooks的运行机制useState、useEffect的依赖数组虚拟DOM和diff算法受控组件与非受控组件组件通信方式key属性的作用useEffect的执行时机会被很多候选人搞混。记住useEffect是在浏览器完成绘制之后异步执行的所以不要依赖它做同步操作。如果需要在DOM变更之后、浏览器绘制之前执行副操作需要用useLayoutEffect。工程化方面webpack的loader和plugin区别是多年不变的经典题vite和webpack对比在2024年也成了高频热点。要记住vite基于原生ESM开发环境无需打包冷启动速度快webpack在开发环境需要全量打包但随着缓存和调优手段成熟两者在大型项目中的差距并没有口口相传的那么夸张。生产环境构建时vite使用Rollup打包webpack自身负责打包压缩。3. 高频编程题实战解析3.1 基础题两数之和与三数之和两数之和算是字节笔试的入场券。题目给一个数组和一个目标值要求找出两个数之和等于目标值的下标组合。最优解用哈希表时间复杂度O(n)。function twoSum(nums, target) { const map new Map(); for (let i 0; i nums.length; i) { const diff target - nums[i]; if (map.has(diff)) { return [map.get(diff), i]; } map.set(nums[i], i); } return []; }注意题目要求返回下标还是返回数值这两个细节处理方式完全不同。返回数值时可以先排序再用双指针返回下标时只能用哈希表。字节笔试里两数之和通常作为第一题难度不高但一定要保证一次通过不要犯数组越界或者漏判重复元素的低级错误。三数之和难度上了一个台阶要求找出数组中和为0的三个数且结果中不能含有重复的三元组。核心思路是先排序然后固定一个数再对后面的区间用双指针同时注意跳过重复值。function threeSum(nums) { const result []; nums.sort((a, b) a - b); for (let i 0; i nums.length - 2; i) { if (i 0 nums[i] nums[i - 1]) continue; let left i 1; let right nums.length - 1; while (left right) { const sum nums[i] nums[left] nums[right]; if (sum 0) { result.push([nums[i], nums[left], nums[right]]); while (left right nums[left] nums[left 1]) left; while (left right nums[right] nums[right - 1]) right--; left; right--; } else if (sum 0) { left; } else { right--; } } } return result; }去重是三数之和最容易丢分的地方一定要在固定数移动时和左右指针移动时都做去重。3.2 高频题最长无重复字符子串这类题在字节笔试中出现频率极高属于滑动窗口类问题。题目给定一个字符串找出其中不含有重复字符的最长子串长度。思路是维护一个窗口窗口内不含重复字符用哈希表记录字符最近出现的下标。function lengthOfLongestSubstring(s) { let left 0; let maxLen 0; const map new Map(); for (let right 0; right s.length; right) { const char s[right]; if (map.has(char) map.get(char) left) { left map.get(char) 1; } map.set(char, right); maxLen Math.max(maxLen, right - left 1); } return maxLen; }这道题的核心技巧在于map中存的是每个字符最近一次出现的下标当遇到重复字符时左指针直接跳到该字符上一次出现的后一个位置。有一个容易忽略的细节是判断map.has(char)时还要加上map.get(char) left条件否则左指针可能回退。这题背下来不难难的是能现场推导出为什么left更新的方式是正确的。3.3 进阶题手写数组扁平化与去重虽然不算严格意义上的LeetCode题但字节笔试题中手写工具函数时有出现。数组扁平化有三种主流写法笔试推荐用reduce递归版function flatten(arr) { return arr.reduce((acc, cur) { return acc.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []); }如果不考虑性能还可以用toString或flat方法但笔试时使用这些内置方法可能被视为违规因为考察点就是你的递归功底。手写深拷贝也是字节前端笔试的常客需要实现函数、Date、RegExp、Map、Set等特殊类型以及循环引用处理。function deepClone(obj, map new Map()) { if (obj null || typeof obj ! object) return obj; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags); if (map.has(obj)) return map.get(obj); const result Array.isArray(obj) ? [] : {}; map.set(obj, result); for (const key of Object.keys(obj)) { result[key] deepClone(obj[key], map); } return result; }深拷贝写出来不难难的是考虑全面。数组对象的循环引用、Symbol属性、不可枚举属性等都需要针对性处理。我建议笔试时先把基础版本写对如果时间充裕再补充边界情况不要一上来就想写完美版本而把自己绕晕。3.4 进阶题手写防抖与节流防抖和节流是前端笔试题中的常青树也是字节特别喜欢考的手写题。虽然代码只有十几行但能否把参数传递、this指向、取消功能都支持好是区分熟手和生手的分水岭。防抖事件触发后延迟执行如果延迟期间再次触发则重新计时。function debounce(fn, delay) { let timer null; return function(...args) { if (timer) clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); timer null; }, delay); }; }节流单位时间内只能触发一次。function throttle(fn, interval) { let lastTime 0; return function(...args) { const now Date.now(); if (now - lastTime interval) { fn.apply(this, args); lastTime now; } }; }注意防抖和节流的this指向问题由于setTimeout中的回调是独立调用this不会自动绑定到调用者所以必须通过apply显式绑定调用者。这个细节很多人会漏掉写出来之后被面试官追问为什么this指向不对就尴尬了。3.5 进阶题手写Promise与事件总线手写Promise是字节笔试中出了名的高区分度题目有的年份直接让你实现Promise.resolve、Promise.all、Promise.race有的年份要求实现一个完整的Promise类。我建议无论如何都要把Promise的核心机制理解透至少能手写Promise.all和Promise.race。function promiseAll(promises) { return new Promise((resolve, reject) { const results []; let resolvedCount 0; for (let i 0; i promises.length; i) { Promise.resolve(promises[i]).then(value { results[i] value; resolvedCount; if (resolvedCount promises.length) { resolve(results); } }).catch(reject); } }); }注意Promise.resolve(promises[i])这行是很多初学者会忽略的边界处理因为传入的数组可能包含普通值而不是Promise对象包一层Promise.resolve可以保证统一处理。事件总线EventEmitter也是高频手写题本质是一个发布订阅模式。核心方法包括on、emit、once、off。class EventEmitter { constructor() { this.events {}; } on(event, callback) { if (!this.events[event]) { this.events[event] []; } this.events[event].push(callback); } once(event, callback) { const wrapper (...args) { callback(...args); this.off(event, wrapper); }; this.on(event, wrapper); } off(event, callback) { if (!this.events[event]) return; this.events[event] this.events[event].filter(fn fn ! callback); } emit(event, ...args) { if (!this.events[event]) return; this.events[event].forEach(fn fn(...args)); } }事件总线的考点在于once方法的实现思路包装一层函数执行完原回调后自动解绑。这个套路在字节面试中会连环追问比如问你多个once同一个事件会怎么执行、解绑不存在的回调会发生什么、可不可以实现一个按照优先级执行的事件总线。3.6 算法题岛屿数量与LRU缓存前端笔试中出现图论算法题的概率越来越高。岛屿数量是一道经典DFS/BFS题给定一个二维数组1代表陆地0代表海洋求岛屿数量。function numIslands(grid) { if (!grid || grid.length 0) return 0; let count 0; const dfs (i, j) { if (i 0 || i grid.length || j 0 || j grid[0].length || grid[i][j] 0) { return; } grid[i][j] 0; dfs(i 1, j); dfs(i - 1, j); dfs(i, j 1); dfs(i, j - 1); }; for (let i 0; i grid.length; i) { for (let j 0; j grid[0].length; j) { if (grid[i][j] 1) { count; dfs(i, j); } } } return count; }这道题的关键点是DFS过程中将访问过的1置为0避免重复计数。如果你熟悉BFS可以同样用队列实现效果一样。笔试时DFS递归代码更简短推荐优先使用递归。LRU缓存是字节笔试中少见的高难度题也是面试阶段的高频题。要求实现一个近期最少使用缓存淘汰算法class LRUCache { constructor(capacity) { this.capacity capacity; this.map new Map(); } get(key) { if (!this.map.has(key)) return -1; const value this.map.get(key); this.map.delete(key); this.map.set(key, value); return value; } put(key, value) { if (this.map.has(key)) { this.map.delete(key); } this.map.set(key, value); if (this.map.size this.capacity) { const oldestKey this.map.keys().next().value; this.map.delete(oldestKey); } } }利用Map对象天然维护插入顺序的特性可以直接实现LRU缓存而不需要手写双向链表。每次get或put时将该键删除再重新插入让它排到队尾超过容量限制时删除队首元素因为队首元素就是最久未使用的。实际项目中这也是实现LRU的高效方案。4. 模拟笔试实战演练4.1 模拟题一前端场景编程题2024秋招某岗位出现了一道综合性很强的场景题给定一组用户操作记录要求设计一个函数统计用户在页面上的操作频率。操作记录是一个数组每个元素为{userId, operationType, timestamp}。要求返回每个用户操作次数最多的三种操作类型。这题思路不复杂但涉及数据聚合和排序很考察代码组织能力。先按userId分组再统计每个用户的操作类型次数最后取前三。function topThreeOperations(records) { const userMap {}; records.forEach(({ userId, operationType }) { if (!userMap[userId]) { userMap[userId] {}; } userMap[userId][operationType] (userMap[userId][operationType] || 0) 1; }); const result {}; for (const [userId, opMap] of Object.entries(userMap)) { result[userId] Object.entries(opMap) .sort((a, b) b[1] - a[1]) .slice(0, 3) .map(([type]) type); } return result; }这种场景题的核心不是考察某个特定算法而是工程化思维能不能把复杂问题拆解成可复用的子步骤。建议先画伪代码再写正式代码字节笔试的编程题环境支持在线调试但不会像LeetCode那样给那么多测试用例边界情况需要自己考虑。4.2 模拟题二前端手写功能题手写一个请求并发限制函数控制最大并发数。这是字节笔试中少有的将业务场景与算法结合的题目很多同学只在面试中被问到过笔试出现的概率也在增大。function limitConcurrency(tasks, limit) { let index 0; let activeCount 0; const results []; return new Promise((resolve) { function next() { if (index tasks.length) { if (activeCount 0) resolve(results); return; } const currentIndex index; activeCount; Promise.resolve(tasks[currentIndex]()).then(result { results[currentIndex] result; activeCount--; next(); }).catch(err { results[currentIndex] err; activeCount--; next(); }); } for (let i 0; i limit; i) { next(); } }); }完成任务的时机判断是这题的难点。所有任务开始后什么时候算全部完成上面的方案是当index超过tasks长度且activeCount为0时说明所有任务已启动且全部执行完毕。这个思路可以推广到请求并发控制、文件分片上传等真实场景。4.3 模拟题三综合设计题字节2024秋招部分岗位出现了一道系统设计题如果让你设计一个前端错误监控SDK你会如何设计请写清采集、上报、展示三个环节。这种题没有标准答案但考察你对前端监控体系的整体理解。我记得一个高分的思路是这样的采集环节通过window.addEventListener(error)捕获未处理的JavaScript异常通过window.addEventListener(unhandledrejection)捕获未处理的Promise异常通过window.addEventListener(error, handler, true)捕获资源加载失败。每一步捕获都需要记录错误堆栈、发生时间、URL、用户信息、浏览器信息等。上报环节采用批量上报策略用Set存储错误对象每10条或者每30秒批量发送一次使用sendBeacon在页面卸载时保证数据送达。如果请求量巨大可以在前端做采样比如只上报10%的错误数据。展示环节需要错误堆栈解析sourcemap反解、错误分组去重、按版本和机型筛选。这个设计题更看重你的产品思维不仅要想到如何采集还要想到后续如何定位问题、如何降低性能损耗、如何防止刷接口。我建议回答这类题时用“采集-传输-分析”三层架构来组织答案这是最清晰的表达框架。4.4 模拟题四兼容JavaScript的算法题字符串压缩是一种非常经典的字符串处理题。给定一个字符串将连续相同字符压缩成字符加出现次数例如aabcccccaaa压缩为a2b1c5a3。如果压缩后的字符串长度不小于原字符串则返回原字符串。function compressString(str) { let result ; let count 1; for (let i 0; i str.length; i) { if (str[i] str[i 1]) { count; } else { result str[i] count; count 1; } } return result.length str.length ? result : str; }这题在字节笔试中出现过虽然难度不高但容易在判断条件上出错。要注意str[i 1]在最后一个字符时为undefined不会等于任意字符因此最后一个字符的计数会被正常处理。另一个容易踩的坑是返回条件题目要求压缩后不小于原长度则返回原字符串所以判断要用result.length str.length而不是大于。5. 备考策略与刷题路线5.1 重点投入方向字节前端笔试的备考我建议把时间分成三块比例大约是5:3:2。50%的时间刷LeetCode热门题尤其是数组、字符串、链表、二叉树、动态规划这五个大类。30%的时间复习前端基础八股包括JS核心机制、浏览器原理、网络协议、HTTP缓存、React/Vue框架基础。剩下20%的时间用来做手写题训练包括防抖节流、深拷贝、Promise、事件总线、数组方法实现等。LeetCode刷题建议优先刷热题100题不需要追求题海战术。字节笔试的算法难度通常不会超过LeetCode中等题但会结合具体的业务场景来出题比如在字符串处理的题目中增加一个前端特有的限制条件。5.2 刷题路线推荐按专题分类来刷效率最高第一周数组与哈希表两数之和、三数之和、最长无重复子串、顺时针旋转矩阵。第二周链表与字符串反转链表、判断回文链表、字符串解码、最长公共前缀。第三周二叉树前中后序遍历、层序遍历、最大深度、最近公共祖先。第四周动态规划与双指针爬楼梯、打家劫舍、接雨水、移动零。每天保持3-5道题的节奏不用贪多但每一道题都要确保能独立写出来而不是看着答案抄一遍。字节笔试编程题的时间大约是每道题30-40分钟如果你能在平时练习时控制在20分钟内AC现场发挥就比较从容。5.3 真实经验体会我在整理这些题目的过程中发现一个规律字节的笔试题目并不偏门但非常考验代码的鲁棒性。同样的功能有人只写了正常路径有人会补充边界情况判断。笔试时多花一分钟检查边界条件往往能多过一个测试用例这个投入是非常值得的。还有一个体会是时间分配很考验心态。编程题如果连续卡壳很多人会慌一慌选择题也没心思好好做了。我的建议是遇到一道题超过15分钟没有思路果断先做下一道把能拿的分拿到手最后回头再看卡住的那道题。字节的编程题是按测试用例给分的过了部分用例也会有分所以即使不能完全AC也要把暴力解法写上去不要留白。6. 常见问题与排查技巧实录6.1 算法题思路正确但代码跑不对这是笔试中最多人遇到的问题代码思路没问题但某些边界情况没处理好导致部分用例失败。排查优先级如下先检查数组越界问题再检查循环边界条件for循环中i length还是i length然后检查空数组和单元素数组的特殊情况最后检查整数溢出问题。举一个实际出现过的例子反转字符串时很多人用for循环 i 0; i str.length / 2但如果在循环内直接拼接原字符串很容易把数组下标搞乱。我的习惯是能写双向指针就用双向指针逻辑更清晰且不容易出错。6.2 手写题环境运行报错笔试题环境通常有多个规范有的平台要求不能使用const/let必须用var有的平台要求代码必须包裹在一个指定的函数里有的平台会自动引入一些工具函数。上传答案前一定要检查一遍自己的代码是否符合题目要求的数据类型格式。比如题目说返回数组你却返回了字符串题目要求输出浮点数保留两位你输出的是整数题目要求数组升序排序你直接用了reverse导致顺序全反。这些低级错误在笔试中比不会解题更可惜。6.3 经典易错知识点自查表防抖和节流的区别以及各自的使用场景get请求和post请求的区别以及数据位置var/let/const的作用域区别apply/call/bind三者的联系与区别深拷贝与浅拷贝的差异数组的splice和slice方法搞混CSS中position的四个值以及定位参考点是body还是最近的定位父级事件冒泡和事件捕获的触发顺序我把这些整理成了一个自查表笔试前一天过一遍会很有帮助。建议你也把容易混淆的知识点积累成一个文档考前集中翻一遍比零散记忆效率高很多。6.4 心态与临场建议笔试时间一般都很紧张遇到不会的题不要慌。我自己的节奏是选择题先浏览一遍把有把握的题先做完不确定的做标记不要浪费太多时间。编程题从题意最清晰的题做起先确保第一道题AC第二道题争取AC第三道题能写多少写多少。字节笔试往年的通过率没有一个明确数字但结合我周围同学的反馈笔试淘汰率大致在70%到80%之间。这意味着你的目标是进入前20%到30%而不是追求满分。一道题AC过一批人两道题AC你已经在安全区三道题AC你会是面试官眼中的高分候选者。根据我个人经历字节笔试最大的特点就是难而公平它不考偏门知识只要你基础扎实、刷题量足够、心态稳定拿下面试机会的概率并不低。尤其是现场讲解思路和复盘真题的时候按专题查漏补缺会为面试积累很多可用的素材。最后分享一个小技巧笔试结束后尽快把题目凭记忆复盘一遍尤其是编程题的实现思路和当时卡住的点。字节面试时面试官经常会让候选人重新讲解笔试中的题目思路这时候如果你复盘过回答会顺畅得多。如果你只想看一条备考建议那就是这条笔试不只是笔试它为后面面试中的所有技术深度追问埋下了伏笔。
返回列表