拓冰建站拓冰建站
首页 / 资讯中心 / 正文

B站前端笔试题全解析:JavaScript基础与浏览器原理实战

1. 卷面整体解析B站前端笔试到底考什么每年秋招季B站的前端笔试题总能在牛客网和掘金上引发一波讨论热度过时了讨论还在继续。原因很简单它的题目不算难但覆盖面很广从JavaScript基础到浏览器原理从框架源码到工程化规范从算法到网络几乎没有明显的“送分题”地带。2023校园招聘前端开发方向笔试卷B整体延续了B站这几年“重视基础、兼顾业务场景”的出题风格。我做完整张卷子之后的第一感受是这份卷子不是在考你背了多少API而是在考你平时写代码的时候有没有真正想过“这行代码背后发生了什么”。1.1 一张卷子的出题逻辑模块分配与隐藏考点先聊模块分配。整张卷子大致由四个部分组成计算机基础与JavaScript核心、浏览器与前端性能、框架与工程化、算法与综合设计。比例上JavaScript和浏览器原理加起来占了接近一半分值框架部分以Vue为主、React为辅算法题基本控制在LeetCode中等难度以内没有出现动态规划、图论这类劝退题。这里有一个容易被忽略的细节B站作为一家视频平台卷子里关于“视频加载优化”“首屏性能”“缓存策略”的题目比重明显高于普通电商或SaaS公司。这说明面试官希望候选人对真实的视频站场景有感知力而不是只会在管理后台里写表格。还有一点值得注意卷面多选题占了不少比例并且很多多选题的选项非常接近。比如“哪些操作会导致回流”这种题四个选项里可能有三个都是对的剩下那个是重绘而不是回流。这种题考察的不是记忆而是你能不能区分概念之间的边界。1.2 这份卷子更适合什么基础的候选人按照卷面难度来推测B站前端校招的定位应该是“基础扎实的应届生”而不是“已经有大厂实习经验的卷王”。如果你能把《JavaScript高级程序设计》前十五章读透把Vue官方文档的进阶部分过一遍再把剑指Offer刷完大半这张卷子拿个及格分问题不大。但如果想拿高分需要在两个地方突出一是手写代码题的边界情况处理能不能一次写对二是综合设计题的方案能不能落地。很多人挂在后者因为平时做的项目要么是跟着教程敲的要么是课程作业基本没思考过“如果让我从零设计这套东西我会怎么做”。我这篇文章会按照卷子的实际考点模块来拆解每道重点题都会给出命题意图、答题思路和参考解法最后再聊聊机考环境中哪些坑是我亲身踩过的。2. JavaScript核心笔试的“重头戏”题库拆解JavaScript相关的题目在B站这份笔试卷B里占据了约35%的篇幅分布在选择题、填空题和手写代码题三种题型中。这其实和大多数中大型互联网公司的出题策略一致——JS基础是前端面试的“照妖镜”候选人有没有真功夫几道题就能试出来。2.1 数据类型与隐式转换一道题能看出基本功卷子里有一道我记得很清楚的题给出[] ![]的结果并要求说明原因。很多人一眼看到空数组和布尔值比较直接写false但正确答案是true。这里的核心考点是JavaScript的隐式类型转换规则。![]会先把数组转成布尔值再取反空数组转布尔值是true取反后变成false。于是原式变成了[] false。根据ES规范布尔值参与相等比较时先转数字false转成0式子变成[] 0。对象和数字比较时对象先转原始值空数组调用valueOf()返回自身不是原始值继续调用toString()得到空字符串字符串再转数字变成0。两边都是0所以结果是true。这类题看起来像是“冷知识”实际上是在考察你对类型转换链条的熟悉程度。我建议大家复习这块时不要把精力放在死记硬背结果上而是把ToPrimitive、ToNumber、ToString这三条转换路径理清楚。我再给一个类似的例子供练习null undefined是true但null undefined是falseNaN NaN是false但Object.is(NaN, NaN)是true。这道题的高分回答不会只给结果而是会把每一步转换依据都写出来包括是依据ECMAScript规范里的哪一张表。面试官看到这种作答方式基本可以确定候选人读过规范或者至少仔细研究过类型转换。2.2 异步与事件循环必考的“输出顺序”题异步编程的题目几乎成了前端笔试的标配B站这份卷子也不例外。它出了一道结合setTimeout、Promise、async/await的输出顺序题我试着还原一下类似的出题风格async function async1() { console.log(async1 start); await async2(); console.log(async1 end); } async function async2() { console.log(async2); } console.log(script start); setTimeout(function() { console.log(setTimeout); }, 0); async1(); new Promise(function(resolve) { console.log(promise1); resolve(); }).then(function() { console.log(promise2); }); console.log(script end);这道题的输出顺序是script start - async1 start - async2 - promise1 - script end - async1 end - promise2 - setTimeout。很多人会在async1 end和promise2的顺序上犯错因为不了解await的底层原理。await会把它后面的表达式当成一个Promise并且将await之后的代码包装成.then的回调。所以async1 end实际上是在微任务队列里执行的。宏观上整个执行流程是同步代码先执行完然后清空微任务队列最后才执行宏任务。我再补充一个高频考点微任务中又产生微任务怎么办浏览器会一直把微任务队列清空才去取下一个宏任务。这意味着如果在微任务里递归创建微任务主线程可能长时间得不到空闲这就是所谓的“微任务饿死宏任务”。B站后来在面试环节也问过这个延伸问题所以大家复习时不光要会背流程还要理解为什么浏览器要这么设计。2.3 手写代码防抖节流、数组乱序与...args展开手写代码题部分B站选了防抖、数组乱序和函数柯里化三道。前两道属于“背模板就能过”的题但想拿满分需要把边界情况想清楚。以防抖为例多数人写的版本是function debounce(fn, delay) { let timer null; return function(...args) { if (timer) clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); }, delay); }; }这版代码在基础测试里没问题但有几个细节值得打磨一是返回函数的this需要透传上面的写法用fn.apply(this, args)已经处理了二是有时候需要支持立即执行即第一次点击立刻触发后面的点击才进入防抖逻辑三是需要考虑取消取消防抖的能力。我带过几次校招新人发现多数应届生能把基本版本写出来但极少有人主动提到“如果delay传成负数怎么办”“组件销毁时定时器怎么清理”这类问题。这些不是考题本身的要求但如果你在作答时主动补充这些边界处理会让阅卷人觉得你有工程经验而不只是会背题。数组乱序的常见解法是使用Fisher-Yates洗牌算法。很多人直接写arr.sort(() Math.random() - 0.5)这种做法有两个问题一是排序算法的实现依赖于浏览器结果不一定是均匀分布的二是它会修改原数组如果题目要求“返回新数组”这种写法就会被扣分。function shuffle(arr) { const result [...arr]; for (let i result.length - 1; i 0; i--) { const j Math.floor(Math.random() * (i 1)); [result[i], result[j]] [result[j], result[i]]; } return result; }...args这个知识点在卷子中出现了多次。它不仅仅是语法糖还涉及剩余参数和展开运算符之间的区别。剩余参数用于收集参数必须放在参数列表最后一位展开运算符用于展开可迭代对象。两者都使用...符号但是作用方向完全相反。这个考点虽然基础但经常有人答错值得单独拿出来复习。3. 浏览器与前端性能视频站独有的考察角度浏览器相关的题目在B站试卷中的占比明显偏高而且出题方向很聚焦渲染流程、缓存策略、性能优化。这其实反映了B站前端团队的真实技术场景——每天要服务大量视频页面首屏速度、播放流畅度、资源加载效率都是业务的核心指标。3.1 渲染流程与回流重绘性能排查的第一步卷子里有一道经典的问答题从输入URL到页面展示中间发生了什么这道题在面经里被写烂了但B站给它加了限定条件——“结合视频站的首屏渲染来分析”。这要求你的回答不能只停留在DNS解析 - 建立TCP连接 - 发送HTTP请求 - 服务器返回HTML - 解析构建DOM树 - 构建CSSOM树 - 合并成渲染树 - 布局 - 绘制这个通用流程上。结合B站场景你需要额外提到几个关键点HTML里的大体积CSS和JS文件是怎么做拆分的、视频封面图是采用什么格式和尺寸、首屏视频帧是否做了预加载、直播场景下WebSocket建立连接的时机如何安排。这种把基础题和业务场景结合的方式是B站笔试的一个显著特点。单纯背八股文的候选人在这里会露馅因为你一旦开始结合场景展开就必须真的理解每个环节的作用而不是机械地背出几个名词。更细一点的回流重绘问题上B站出了一道多选题以下哪些操作会触发回流选项包括修改元素的宽度、改变字体大小、获取offsetHeight、修改背景颜色。前三个都会触发回流最后一个只触发重绘。这里容易出错的是“读取布局属性”也会触发回流。因为浏览器为了返回准确的值需要强制进行一次布局计算这会打断渲染流水线的优化。性能调试时常见的做法是先用getBoundingClientRect()一次性读取需要的值再统一做修改避免在循环里反复读写导致“布局抖动”。3.2 缓存策略与视频加载优化B站场景的前端思维B站对缓存策略的考察不是问“HTTP缓存有哪些字段”而是给了一个具体场景视频首帧加载慢你会如何优化首先要理清楚缓存层级。浏览器缓存可以大致分为内存缓存、磁盘缓存、Service Worker 缓存和 HTTP 缓存。内存缓存速度最快适合放当前页面还会用到的资源磁盘缓存适合放长期复用的静态资源Service Worker 可以做离线缓存和资源预取HTTP 缓存又分强缓存Cache-Control、Expires和协商缓存Last-Modified、ETag。针对视频首帧加载慢的问题合理的优化方向有几个第一把封面图和首帧数据从视频文件中剥离利用HTTP强制缓存提前加载第二使用preload预加载关键资源使用preconnect提前与视频CDN建立连接第三对小体积视频采用分片策略播放器先请求首片快速打开后再并行加载后续分片第四在遇到重复访问的情况时启用Service Worker缓存视频分片减少回源请求。你需要明白“减少请求”和“加快请求”是两条不同的优化路径。减少请求靠缓存加快请求靠并行和预连接。B站这种高并发、高流量的场景两者缺一不可。试卷里这道题满分作答应该把两个维度都覆盖到并给出具体的实现方式。4. 框架与工程化Vue/React原理和项目规范化框架部分的题目围绕Vue展开较多React集中在Hooks相关知识点。考虑到B站前端技术栈以Vue为主这个出题比重在预期范围内。4.1 Vue响应式原理从Object.defineProperty到ProxyVue 2的响应式原理几乎是必考题B站也不例外。大部分人都能写出Object.defineProperty递归劫持对象属性同时考虑到属性新增删除、数组索引和长度变化无法被拦截的局限性。但卷子里的问题更进了一步Vue 3用Proxy重写之后解决了哪些问题又带来了哪些新的注意事项回答这个问题时要先把Vue 2的缺陷答完整无法监听属性的新增和删除、无法监听数组索引直接赋值、初始化时递归遍历对象性能开销大。然后再说Proxy的优势可以代理整个对象而不是单个属性、可以拦截has、deleteProperty、ownKeys等更多操作、惰性代理避免初始化时的深度递归。但用了Proxy之后代理对象和原始对象之间的引用关系需要特别留意。在Vue 3中如果你把响应式对象解构出来赋给一个普通变量会失去响应式因为解构拿到的是原始值而不是代理对象。解决方式是使用toRefs。类似的坑还有reactive只能代理对象不能直接代理原始类型需要用到ref。这背后是Proxy必须作用于对象而原始类型没有可代理的载体。4.2 组件通信与设计模式业务代码的抽象能力组件通信题采用了填空题形式父子组件、兄弟组件、跨级组件分别有哪些通信方式。父子组件用props和事件兄弟组件用父组件中转或者事件总线跨级通信用provide/inject或者状态管理。这道题本身不难但B站在后面加了一问“选用事件总线需要注意哪些问题”。这就考察到实践经验了。事件总线本质是一个发布订阅器它的优点是用起来直接缺点是事件名难以维护、调试时不能直观看到调用链、组件销毁时如果没有取消订阅容易造成内存泄漏。在大型项目里滥用事件总线会让系统变得“越改越乱”这也是为什么Vue 3官方不再推荐事件总线作为常规通信方案。我在实际工作中踩过一个相关的坑用事件总线通知兄弟组件刷新列表结果忘了在组件销毁时off导致每次路由切换都累积一个监听器。单次触发没问题但用户多切换几次页面后列表刷新逻辑会被执行多次接口被重复请求。排查了很久才发现是事件监听器堆积导致的。4.3 工程化规范代码风格、构建工具与版本管理工程化部分的题目不多但很关键其中有一道是关于ESLint和Prettier的职责区分。很多新人对这两个工具的理解是重叠的都是格式化代码。但它们在定位上有本质区别。ESLint的核心能力是静态检查它关注“代码有没有错误风险”比如未使用的变量、隐式的eval调用、全局污染等它也能做格式检查但这不是它的核心价值。Prettier只做格式化它不管你代码有没有逻辑问题只负责把代码长得整整齐齐。一个常见的配置组合是ESLint负责“抓问题”Prettier负责“美化和统一风格”两者通过eslint-config-prettier关闭ESLint中与格式化相关的规则避免规则冲突。运行时用eslint --fix做自动修复提交前用lint-staged结合husky对暂存区文件做检查。这套流程已经成为目前前端工程化的“标准三件套”之一B站考一道相关题目其实是在筛选有真实项目经验的候选人。另外卷子还提到了模块化开发。CommonJS和ESModule的区别是个老生常谈的问题但有几个细节值得强调CommonJS是同步加载ESModule是异步加载CommonJS输出的是值的拷贝ESModule输出的是值的引用CommonJS在运行时确定模块依赖关系ESModule在静态分析阶段就能确定。最后一点是ESModule可以做 tree-shaking 的基础这也是为什么打包工具都会优先处理ESModule语法的原因。5. 算法与计算机网络笔试的筛选池算法题在这份B卷里占比不算高大约两道左右但难度集中在“高频变形题”上。计算机网络部分则混合在选择题里单独考察的不多不过基础概念该考的一个没少。5.1 高频手写算法题不刷题真的会吃亏B站算法题的风格和字节跳动、阿里那种“硬核数据处理”风格不一样更偏向“业务开发中真会碰到的问题”。让我印象最深的一道是给定一个数组求连续子数组的最大和要求输出最大和以及对应的起止下标。这其实是LeetCode 53的有界变形。基础版解法是Kadane算法核心思路是在遍历过程中维护两个变量currentMax表示以当前元素结尾的最大子数组和globalMax表示全局最大和。每次迭代时currentMax Math.max(nums[i], currentMax nums[i])然后更新globalMax。如果要输出起止下标就需要在更新currentMax时判断当nums[i]本身更大时说明以当前元素作为新的起点否则延续之前的起点。当globalMax被更新时记录当前起点和终点。这个“额外记录下标”的变形在面试中很容易被问到建议大家手动写一遍完整版本不要只记核心那段转移方程。function maxSubArray(nums) { let currentMax nums[0]; let globalMax nums[0]; let start 0, end 0, tempStart 0; for (let i 1; i nums.length; i) { if (nums[i] currentMax nums[i]) { currentMax nums[i]; tempStart i; } else { currentMax currentMax nums[i]; } if (currentMax globalMax) { globalMax currentMax; start tempStart; end i; } } return { sum: globalMax, start, end }; }另一个高频题是手写一个Promise.all。这个题目考察的点很多返回一个Promise、入参是可迭代对象、处理空数组时直接resolve、任何一个Promise失败就立刻reject、结果数组要保持输入顺序。以下是参考实现function promiseAll(promises) { return new Promise((resolve, reject) { const results []; let count 0; if (promises.length 0) { resolve(results); return; } promises.forEach((promise, index) { Promise.resolve(promise).then(value { results[index] value; count; if (count promises.length) { resolve(results); } }, reject); }); }); }这道题的失分点集中在两点忘记用Promise.resolve包裹非Promise值以及结果数组不是按完成顺序而是按输入顺序填充。前者会导致传入一个普通值时报错后者会引发取值的bug。5.2 网络基础HTTP缓存、跨域与WebSocket网络相关题目不算多但有一道跨域的题目让我觉得很有代表性服务器设置了Access-Control-Allow-Origin: *为什么浏览器仍然拦截了请求这题考察的是对CORS机制的理解。服务端配置只解决了一部分问题浏览器还会根据请求类型做不同的处理。如果触发的是非简单请求比如携带了自定义Header、使用PUT/DELETE方法或者Content-Type不是简单类型浏览器会先发一个OPTIONS预检请求检查服务器是否允许实际请求的头部和方法。如果服务器对预检请求的响应里没有包含对应的Access-Control-Allow-Headers或Access-Control-Allow-Methods即使设置了允许所有域名浏览器的真正请求仍然会被拦截。另一个与B站业务直接相关的点是WebSocket。弹幕系统在早期版本中依赖HTTP轮询后面逐步迁移到WebSocket。笔试里问的是“WebSocket相对于HTTP的优势”这题的基础答案有全双工通信、头部开销小、服务端可以主动推送数据。结合B站场景可以补充一点弹幕消息的实时性要求高HTTP轮询要么延迟大要么同时建立大量请求造成资源浪费WebSocket通过长连接能同时解决这两个问题。还有HTTP缓存相关的题目我在第3部分已经展开过这里不重复。但网络题的复习策略需要单独提一句不要盯着“协议细节”死磕HTTP协议的状态码含义、缓存字段、CORS预检过程和WebSocket的握手过程这四个点覆盖了大多数前端笔试网络题的80%考区优先把它们吃透更重要。6. 综合题与项目复盘拉开差距的非编码题B站这份卷子和很多公司不一样的地方是它在最后设置了一道综合设计题并且这道题的权重还不低。题型不是让你写代码而是给一个业务场景让你做技术方案设计。这种题在校园招聘里不多见因为通常被认为对经验要求过高但B站坚持出说明他们更关注候选人的思维方式和设计思路。6.1 系统设计题怎么答从“视频上传”到“弹幕系统”原题我记得大概是请设计一个短视频分享平台的内容上传功能要求考虑大文件上传、断点续传、不同网络环境下的体验优化。这是一道非常典型的“业务场景设计题”解法没有一个固定的标准答案但回答问题的框架是有迹可循的。第一步要明确约束条件大文件是指多大的文件是几百MB还是几个GB如果是几个GB的视频直接使用常规HTTP上传会出现两个问题内存占用过高和上传中断后需要从头开始。解决方案是分片上传把文件切成固定大小的分片例如每个分片5MB然后并发上传多个分片。服务端接收完所有分片后触发合并任务生成完整文件。第二步要处理断点续传。核心思路是在本地记录已经上传成功的分片序号重新上传时先向服务端请求“当前文件哪些分片已经就绪”然后只上传缺失的分片。实现方式是在发起上传之前先调用一个GET /upload/status?fileIdxxx接口拿到已上传分片列表。上传过程中每个分片完成后更新本地记录这个记录可以维护在一个对象里也可以借用localStorage实现刷新后的恢复。第三步是优化弱网环境下的体验。比如动态调整并发数网络好的时候同时传6个分片网络变差后降为2个避免请求排队导致全部超时。再比如配合Web Worker做分片逻辑避免大文件切割时阻塞主线程导致页面卡顿。这些细节在阅卷时属于“加分亮点”层级因为普通候选人能想到分片上传已经不错了能继续往下考虑网络适应性的不多。面试问这类题时一个好的加分项是主动询问接口定义的细节。比如问清楚“服务端是上传完成后通知还是每个分片上传完成都通知”“合并任务是由服务端触发还是客户端触发”。这些问题会让人感觉你确实设计过类似的系统而不是只会背八股。6.2 项目复盘题STAR法则之外还要准备什么综合题之后还有一道项目自述类的题通常是让你挑一个自己做过的项目讲讲你在里面的角色、遇到的困难、如何解决以及如果重做会怎么改进。这道题看起来没有技术含量但据我所知很多候选人在这一题上丢分严重。丢分原因通常有几种讲的项目不是自己做的一问细节就露馅项目规模太小没有技术深度只讲“做了什么”不讲“为什么这样做”。这三种情况只要中了一个基本和高分无缘。我在指导师弟师妹时始终强调一个原则哪怕项目再小也要提炼出至少两个“在工程上有价值”的点。比如你写了一个ToDo List那你可以讲讲“为什么选择用本地存储而不是全局状态管理”“批量更新时如何避免不必要的组件渲染”“数据量增大之后的性能瓶颈会出现在哪里”。这些问题的答案能展示你有思考过项目背后的原理。如果项目有真实的难点和踩坑记录就更好了。一个页面渲染大量列表导致的卡顿你是怎么定位到瓶颈的是通过浏览器Performance面板看到脚本执行时间过长还是通过React Profiler定位到某个组件反复渲染定位后是如何优化的是加了虚拟列表还是改进了数据对比逻辑这些问题比“你的项目有哪些功能”更有价值因为面试官真正想了解的是你的解决问题的能力。7. 备考策略与避坑记录写到这里卷子上的内容差不多拆完了。最后我想聊聊备考过程中的策略安排和机考环境下的实操细节。这部分内容很大程度上来自我自己的经历也来自这些年带新人的观察算是面经之外少有系统整理的内容。7.1 时间分配与刷题顺序如果你距离笔试还有一个月左右我建议按“四三三”原则分配时间40%花在JavaScript基础和浏览器原理上30%花在框架和工程化相关知识点上30%花在算法刷题和计算机网络复习上。这样安排的原因是前端笔试的得分大头在基础题基础题也是面试环节最容易被追问的部分值得优先准备。刷题顺序上先做剑指Offer的高频题再刷LeetCode Hot 100里难度在“简单到中等”的部分最后集中训练手写题。手写题优先级最高的是防抖节流、深拷贝、数组去重、Promise相关函数、柯里化、数组扁平化。这些题目在B站和多家中大厂的笔试卷里反复出现性价比极高。框架部分的复习不要只依赖官方文档。对于Vue至少要做到能说出响应式原理、虚拟DOM的diff过程、组件间通信的多种方案、生命周期钩子的使用场景。对于ReactHooks的工作原理和常见错误用法要能聊清楚比如useEffect的依赖数组对执行时机的影响、useMemo和useCallback各自的适用场景。7.2 我自己踩过的坑机考环境与提交规范机考时的坑比想象中多而且很多和代码水平无关纯属“环境经验不足”。我在参加一场在线笔试时因为没注意代码编辑器默认不允许console.log调试输出测试用例里出现了日志信息就提交了结果系统判定格式错误十几分钟才定位到问题。后来我养成了几个习惯建议大家提前模拟练一下。第一写完代码后把所有的调试日志删掉或注释掉防止输出格式被误判。第二输入输出一定按照题目要求来多一个空格、缺一个换行都可能导致判题不通过。第三如果平台支持本地IDE编写也要在本地用Node或浏览器跑一遍示例用例不要直接粘贴上去就提交因为系统环境可能使用不同的Node版本某些新语法可能不支持。还有一个容易被忽略的点答题时注意看每道题的分值和时间限制。遇到一道题卡住超过15分钟先跳过做后面的不要跟一道题死磕。笔试的时间分配往往是整体性的前面的选择题耗时较少后面的综合题需要预留大量时间思考和作答。如果在一道手写题上纠结太久后面设计题来不及做很影响总分。我个人在实际操作中的体会是B站笔试卷B的整体风格其实代表了当前大厂前端校招的主流方向重基础、重场景、重设计思维。基础扎实、能解决真实业务问题、有良好工程习惯的候选人通常能在这份卷子里拿到不错的成绩。与其焦虑地刷题不如静下心把每个知识点的原理弄明白因为面试和笔试最终检验的都是你真正理解了多少而不是你背下了多少。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门