
每年秋招一到笔试题就是技术群里聊得最凶的话题。哔哩哔哩2019秋招技术岗前端、运维、后端、移动端第三套笔试题我印象挺深。这套卷子四个方向全占了难度不算变态但覆盖面很扎实几乎没有偏题怪题全是平时写代码绕不开的核心知识点。我当年拿它当过面试官参考题后来也用来带新同学做复盘可以说对这套题的价值还挺有发言权。这份卷子适合谁看应届生准备春招和暑期实习的工作两三年想回头补基础的还有正在帮团队出笔试题的人都能从里面找到自己想要的东西。网上流传的版本细节多少有些出入我按四个方向的高频考点和典型题型做还原式拆解重点把每道题的解题思路和背后的知识链讲清楚。下面直接进正题。1. 试卷结构与考查逻辑1.1 四个方向放在同一张卷子里考的是同一套底层能力前端、运维、后端、移动端看起来是四个岗位但B站这套卷子把四个方向放在一起命题不是为了凑题量。它真正考察的底层能力高度一致操作系统、网络协议、数据结构、并发处理、问题排查手段。差别只在于每个角色把这些知识落在哪一层。举一个例子你就明白了。HTTP缓存前端方向考的是浏览器缓存机制和强缓存/协商缓存头的设置后端方向考的是响应头配置和服务端如何下发ETag运维方向考的是用curl和tcpdump抓到真实请求去验证缓存是否命中移动端方向考的是WebView和原生网络栈对缓存的利用差异。同一个知识点四个视角这才是这套卷子真正想筛选的人不要求你全栈但要求你在自己的方向上有完整的技术纵深。1.2 校招笔试的难度定位基础概念加场景应用校招笔试不会像社招那样大量考项目细节因为应届生普遍没有太多项目沉淀。这套卷子的题目设置是“基础概念 场景应用”的复合结构。基础概念题用来过滤背题党比如事件循环输出顺序、Activity启动模式场景应用题用来过滤没真正写过代码的人比如大文件上传的并发控制、线上Full GC排查。这类题型的比例通常会控制在1:1左右。只答对基础题不够只堆场景分析没有概念支撑也拿不了分。很多时候你会发现把概念题理解透场景题就是概念的延伸两者是同一棵知识树上的不同分支。1.3 第三套题的整体风格偏工程落地比起纯记忆性的八股这套题更偏向“问题能不能落地”。比如手写并发控制、Web Worker上传分片、K8s调用containerd的完整链路、移动端性能优化方案都属于那种“你做过就会没做过只能靠猜”的题目。这也是B站笔试的一个特点技术栈偏业务实际不喜欢纯理论背诵。从题目分布看前端集中在JS异步、浏览器缓存、工程化、移动端适配后端集中在Java基础、Spring、MySQL、Redis、分布式事务运维集中在Linux、网络、负载均衡、日志分析、K8s移动端集中在生命周期、性能优化、网络库、跨端方案。下面分章节展开。2. 前端方向异步、缓存与移动端的三大高频考点2.1 事件循环输出顺序题画队列比背答案靠谱前端部分最经典的还是事件循环输出顺序题。这类题有两个作用一是验证你对JavaScript运行机制的理解二是看你有没有排查异步问题的思路。先看一个我在复盘时经常用的变体和当年那道题的考点几乎一致。console.log(start); async function async1() { await async2(); console.log(async1 end); } async function async2() { console.log(async2 end); } async1(); new Promise(resolve { console.log(promise); resolve(); }).then(() { console.log(promise then); }); setTimeout(() { console.log(setTimeout); }, 0); console.log(end);输出结果是start - async2 end - promise - end - async1 end - promise then - setTimeout。这里有两个特别容易踩的坑。第一个是async2函数体内的console.log是同步执行的所以它在调用async1之后立刻输出第二个是await之后的代码相当于then回调会被注册为微任务而且async1里的await比后面Promise的then先注册所以async1 end先于promise then输出。setTimeout是宏任务无论注册先后都会在所有微任务之后执行。我的建议是遇到这种题别凭感觉硬记答案直接在草稿纸上画三个队列同步代码、微任务队列、宏任务队列。按代码执行顺序往里填最后按“同步 - 微任务 - 宏任务”的顺序输出。养成这个习惯之后几乎所有变体题都能应对。2.2 HTTP缓存设计题强缓存与协商缓存的正确姿势HTTP缓存是前后端都会考的知识点但笔试卷子里出题方式通常是“给你一个视频列表接口让你设计缓存方案”。这种题要拿全分必须把两套缓存机制讲清楚。强缓存依赖Cache-Control和Expires。Cache-Control的max-age表示秒数相对时间优先级高Expires是绝对时间有客户端时间不准的问题现在已经不推荐单独使用。协商缓存依赖ETag和Last-Modified。ETag是内容hash精度高Last-Modified是文件最后修改时间精度到秒。浏览器发起请求时如果资源过期会带上If-None-Match或If-Modified-Since服务器返回304表示资源没变浏览器从本地缓存读取。实际场景中对于打包后的静态资源比如JS和CSS文件名带内容hash直接用Cache-Control: public, max-age31536000, immutable因为文件名变了就是新文件不可能存在更新问题。对于接口数据比如视频列表可以返回短时间的强缓存比如60秒再配合ETag做协商缓存避免频繁304。这个思路放到今天依然标准也是前后端分离项目里最常用的配置。2.3 手写并发控制异步限流的经典实现B站这类公司对并发控制的要求很实际上传视频要分片、批量请求要限流、接口要控制并发数。笔试题经常出现“实现一个并发控制函数同时最多执行N个任务”。这个函数不难但写得好不好很见功底。下面是一个比较经典的实现用了一个共享索引来分配任务核心思路是启动N个worker线程每个worker不断从队列里取任务执行直到取完为止。async function runWithLimit(tasks, limit) { const results new Array(tasks.length); let index 0; async function worker() { while (index tasks.length) { const current index; results[current] await tasks[current](); } } const workers Array.from( { length: Math.min(limit, tasks.length) }, worker ); await Promise.all(workers); return results; }这个实现有几个加分点。第一用index实现原子取任务避免多个worker拿到同一个任务第二结果按原顺序存放在results里函数返回顺序和任务传入顺序一致第三Math.min(limit, tasks.length)处理了limit大于任务数的情况不会创建多余worker。笔试时能把边界情况考虑到位比单纯写出主流程更让面试官放心。2.4 大文件上传Web Worker、分片与断点续传大文件上传是前端场景题里的常客。B站视频投稿对上传的要求很高这道题考的就是“几十GB的视频怎么传到服务器”。首先要明确分片的意义。整文件上传有两个致命问题一是内存占用高文件还没读完浏览器先崩了二是失败后要全部重来。分片后每片单独上传哪片失败重传哪片这就叫断点续传。分片大小一般取2MB到10MBB站这类视频平台通常控制在1到4MB因为分片太小网络请求过多分片太大失败重传成本高。计算文件唯一标识是第二个考点。一般会对文件内容做hash计算但几十GB的文件全量hash会卡住主线程。经验做法是采样计算取文件开头、中间、结尾若干块加随机几块合成一个样本再对样本做hash既能标识文件又不会卡UI。因为hash计算涉及大量文件读取最佳实践是用Web Worker在后台线程做让主线程保持响应。这就是“前端用worker上传大文件”的核心价值。上传流程是前端分片Worker算hash先请求服务端问“这个hash存在吗”存在就直接秒传不存在就逐片上传每传完一片服务端记录进度中断后再次询问服务端返回已上传的分片列表前端从断点继续传。2.5 ECharts移动端折线图渲染完成后显示最后一个点的tooltip这是个很细的问题但能看出来你是不是真的在移动端调过图表。需求是折线图渲染完成后自动展示最后一个数据点的tooltip而不是等用户点击。ECharts提供了一个dispatchAction方法可以在图表上主动触发动作。结合finished事件可以在动画渲染完成后把tooltip弹出来。const chart echarts.init(document.getElementById(chart)); chart.setOption(option); chart.on(finished, () { const lastIndex option.xAxis.data.length - 1; chart.dispatchAction({ type: showTip, seriesIndex: 0, dataIndex: lastIndex }); });这里有几个容易踩的坑。finished事件在ECharts中是动画结束后的回调如果图表没有动画或者你监听的时机不对事件可能不会按预期触发所以一定在setOption之前或之后立即注册监听。另外移动端tooltip的样式要做适配trigger可以设置成axis让移动端手势滑动时能沿着X轴查看所有数据点。如果你在代码里发现showTip后tooltip一闪而过大概率是dispatchAction在重绘后被覆盖需要把dispatchAction放到setOption的第二个参数回调里或者确保调用时机在渲染完成之后。2.6 移动端H5调试vConsole的正确打开方式热词里有个问题我很喜欢vConsole如何在移动端浏览器任意页面插入使用。这基本是移动端开发必踩的现场。电脑上DevTools再好用真机上还是要靠vConsole。常规做法是在代码入口按环境注入SDKif (location.origin.includes(test) || location.hash.includes(debug)) { const script document.createElement(script); script.src https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.1/vconsole.min.js; script.onload () new VConsole(); document.head.appendChild(script); }如果是别人写的线上页面你又没有源码权限可以通过抓包工具或者代理脚本在HTML响应里注入这段脚本实现“任意页面插入”。这里有两个注意点第一生产环境严禁默认开启vConsole会挂载全局对象并拦截console对性能有影响也容易暴露调试信息第二真机联调时vConsole是快捷查看网络请求和System日志的利器但定位完问题一定要关掉这是我带项目时反复强调的规范。3. 后端方向Java基础、数据库与分布式的一致性之问3.1 Java运行时内存区域与Full GC排查后端方向的笔试Java内存模型是必考项。要答清楚堆、虚拟机栈、本地方法栈、方法区JDK8后是元空间各自存什么对象创建和GC的流程是什么。但光背这些不够B站这套题更想看到的是你如何排查问题。典型场景题是线上Java服务频繁Full GC怎么定位标准排查路径是这样的。先用jstat -gcutil查GC频率确认Full GC次数和时间是否异常再用jmap -dump:formatb,fileheap.hprof导堆快照用MAT工具分析大对象和引用链找到占用内存最多的对象往往就能定位到代码位置。如果怀疑是线程问题可以配合jstack看线程状态排查是否有死锁或大量阻塞。笔试要点是把这个排查流程写完整而不是只说一个jmap。面试官真正想确认的是你有没有处理线上问题的现场感这类题是最能拉开差距的。3.2 Spring Bean生命周期与循环依赖Spring Bean的生命周期也是后端高频题考察的是你对框架底层的理解。完整流程可以归纳为实例化构造器- 属性填充 - Aware接口回调 - BeanPostProcessor的postProcessBeforeInitialization - InitializingBean或PostConstruct - BeanPostProcessor的postProcessAfterInitialization - 正常使用 - 容器销毁时执行DisposableBean或PreDestroy。笔试时要注意几个细节。第一postProcessBeforeInitialization执行顺序在InitializingBean之前很多人在这里记反。第二构造器注入的循环依赖Spring无法解决因为Bean还没实例化完就互相引用而setter注入和字段注入可以靠三级缓存解决。第三凡是涉及代理对象的创建比如Transactional都是在postProcessAfterInitialization阶段完成的所以这个阶段可能返回代理对象。如果你能把这些细节说明白说明不是背的流程而是真的理解Spring容器的运行过程。3.3 MySQL索引失效与慢SQL排查MySQL是后端笔试题库里的常客B站这类内容型平台尤其看重数据库设计能力。索引失效的题目通常是给几个SQL判断哪个走索引哪个全表扫描。常见的失效场景有违反最左前缀原则、对索引列使用函数或计算、隐式类型转换比如字符串列用数字查询、like以%开头、or两边的字段没有都建索引、范围查询右侧的索引列失效。光知道失效场景不够还得会看执行计划。explain是必会工具笔试时给出sql让你分析type、key、rows、Extra这几个字段。type从好到差依次是system const eq_ref ref range index ALL看到ALL基本就是全表扫描了。Extra里有filesort或using temporary都要重点警惕。举个例子一个视频表有author_id, status, create_time三个查询条件如果建了三个单列索引MySQL通常只选择一个最优索引其余条件靠回表过滤。这种情况下建议建联合索引把等值条件的列放前面把范围或排序的列放后面。这是面试官最希望看到的优化思路比单纯背失效规则高级得多。3.4 Redis缓存穿透、击穿、雪崩与分布式锁这三个概念每年必考。穿透是查询一个不存在的key请求直接打到数据库击穿是某个热点key过期瞬间大量请求涌入雪崩是大批量key同时过期导致数据库压力暴增。解决方案要能讲清区别。穿透用布隆过滤器在请求前拦截不存在的数据或者对空值也做短暂缓存。击穿热点key用互斥锁让只有一个请求去重建缓存其他请求等待或者用逻辑过期value里存过期时间拿到的数据不是真的物理过期而是一旦发现逻辑过期就异步重建。雪崩给过期时间加随机值避免同一秒内大量key集体过期或者用多级缓存本地缓存挡一层。分布式锁也是高频题标准答案是SET lock_key unique_value NX EX 30。NX保证只有第一次设置成功EX设置过期时间防止死锁unique_value用于释放锁时校验是不是自己加的锁。释放锁必须用Lua脚本原子地比较值再删除防止误删别人的锁。如果要求高可用可以提Redisson的看门狗机制它会自动续期避免业务没执行完锁就过期了。能答到这里这道题基本就拿住了。3.5 前后端分离场景下的SSE跨域问题热词里有一条“SSEEmitter后端本地启动前端无法获取数据”这个场景在前后端分离项目里太典型了。后端用SSEServer-Sent Events推送数据本地起服务用curl测没问题前端fetch或EventSource却拿不到数据。问题通常出在两个点。第一是跨域EventSource是浏览器API受同源策略限制必须让后端的响应里带上Access-Control-Allow-Origin头。第二是SSE的Content-Type必须是text/event-stream且不能用普通fetch的解析方式去读。如果用了Nginx代理还要确认代理配置关掉了缓冲因为SSE是流式响应Nginx默认可能会缓冲住直到连接结束才吐数据这就导致前端一直等不到第一条消息。Nginx正确配置是proxy_buffering off; proxy_cache off; proxy_set_header Connection ; proxy_http_version 1.1;笔试答到这个层面说明你不仅能启动一个SSE服务还理解线上环境里流式响应被缓冲、被代理拦截的完整链路这在前后端分离项目中是非常有价值的经验。4. 运维方向从一条命令到一条调用链4.1 Linux高CPU与内存问题排查运维方向第一道高频题通常是给你一台线上机器CPU飙到99%让你临场排查。这种题笔试考察的是命令是否熟练思路是否清晰。最常用的组合是top、ps、top -Hp、jstack。先用top找到CPU占用的进程PID再用top -Hp PID查具体是哪个线程在跑接着把线程ID转成十六进制最后用jstack抓线程栈在栈里搜索该nid定位到具体代码行。整个链路是这样的top # 找到占用CPU高的PID top -Hp PID # 找到CPU高的线程ID printf %x\n 线程ID # 转十六进制 jstack PID | grep -A 20 nid0x...内存问题则用free -m看物理内存剩余vmstat看swap和内存变化dmesg | grep -i oom查内核OOM日志定位是不是有进程被系统杀掉。这套命令组合是Linux运维的基础功一定要练到条件反射。4.2 网络排障用户说“视频加载慢”怎么办运维的排障题往往给一个实际场景用户反馈视频加载很慢你作为运维工程师怎么定位。这种题没有标准答案但有一个通用的排查顺序。第一步用dig或nslookup确认域名解析是否正常解析延迟高不高。第二步用curl -v看请求细节重点关注DNS解析耗时、TCP建连耗时、TLS握手耗时和TTFB首字节时间。第三步用ping和mtr看链路质量mtr能同时看每一跳的丢包率和延迟如果某一跳丢包严重基本就能定位到网络瓶颈。第四步必要时用tcpdump在服务器端抓包分析是否有TCP重传、窗口缩小等异常。我在实际排障中常用curl -w自定义时间输出把耗时拆到具体阶段curl -o /dev/null -s -w \ dns:%{time_namelookup} tcp:%{time_connect} tls:%{time_appconnect} ttfb:%{time_starttransfer} total:%{time_total}\n \ https://example.com/video.mp4如果total远大于ttfb说明下载传输阶段慢可能是带宽或服务器出口问题如果ttfb本身就慢说明问题在后端处理要往业务代码上查。这个思路比单纯看ping结果准确得多。4.3 负载均衡四层与七层的选型对比运维方向的负载均衡题往往让你对比LVS和Nginx的适用场景。答这道题的关键是讲清楚四层和七层的区别以及为什么很多企业用两层结构。LVS工作在内核态基于TCP/UDP的四层转发性能极高不关心HTTP内容可以做入口流量分发。Nginx工作在用户态是基于HTTP的七层代理能解析URL做路由、限流、SSL终止、重写等。实际生产环境最常见的是LVS加Keepalived做VIP漂移和高可用Nginx在LVS后面做七层负载和业务路由。健康检查也是考点。LVS的Keepalived通过健康检查脚本探测后端是否存活Nginx则可以用upstream_check_module模块做主动探测。答题时如果能补充“主备切换时VIP漂移会让长连接断开需要客户端做重连容错”这类细节说明你真的在运维环境里踩过坑。4.4 日志分析百GB日志里找出异常线上故障处理时日志是唯一能还原现场的资料。笔试考日志分析通常是给你一个访问日志的格式让你用命令统计某个接口的响应时间分布或者找出错误最多的请求。比如access.log的每一行包含时间、请求路径、状态码、响应耗时。要快速算接口的P99耗时用awk加sort加awk三步就能搞定awk {print $NF} access.log | sort -n | awk {a[NR]$1} END {print a[int(NR*0.99)]}如果想统计某个API的5xx错误比例grep /api/video/part access.log | awk {print $9} | sort | uniq -c | sort -rn这类命令组合在笔试里多写一步解释比如sort -n是按数值排序而不是字典序、int(NR*0.99)是取第99百分位的位置都会让答案显得更专业。千万别只会grep单关键字组合拳才是运维的日常。4.5 Kubernetes如何调用containerd从Kubelet到runc的完整链路这几年K8s成了运维方向必考内容B站这份卷子相关热词里就有人在问“Kubernetes是如何调用containerd的从原理到实体调用架”。答案的核心是一条调用链kubelet通过CRIContainer Runtime Interface调用containerdcontainerd再通过shim调用runc最终runc启动容器进程。展开来说当一个Pod被调度到节点上时kubelet会调用containerd的CRI gRPC接口比如创建PodSandbox、创建容器。containerd内部为每个容器启动一个名为containerd-shim-runc-v2的进程这个shim进程的作用很关键它是containerd和容器进程之间的桥梁负责把容器的标准输入输出重定向到日志文件同时支撑容器运行时升级时不影响正在运行的容器。shim再调用runcrunc基于OCI规范用Linux的namespace和cgroup创建隔离的容器进程。排查Pod一直ContainerCreating的问题时这条链路的理解能帮上大忙。常见做法是kubectl describe pod看事件再用crictl ps -a看容器状态用crictl logs查看容器日志。crictl和docker命令的对应关系要熟crictl images对应docker imagescrictl ps对应docker pscrictl inspect对应docker inspect。如果你能说出runc创建进程时调用了clone和unshare这类系统调用这道题基本就是满分答案了。5. 移动端方向生命周期、性能与网络层的那些事5.1 Activity启动模式与应用场景移动端笔试Android的Activity启动模式是必考基础题。standard、singleTop、singleTask、singleInstance四种模式要能区分应用场景。standard是默认模式每次启动都创建新实例singleTop是栈顶复用适合接收通知栏点击这种可能产生重复页面的场景singleTask是栈内复用适合APP的主页或者视频播放页因为全局只需要一个实例singleInstance是独立任务栈适合需要与主栈隔离的页面比如来电界面。笔试容易考到的一个细节是singleTop和singleTask的复用不会走onCreate而是走onNewIntent。所以如果你在播放页里依赖onCreate重新加载数据用singleTask启动模式时就会发现从通知栏跳转过来数据没刷新。正确做法是在onNewIntent里重新读取intent数据并刷新页面。这个细节是区分有没有写过多Activity的试金石。5.2 Handler机制与内存泄漏Handler是Android消息机制的核心也是内存泄漏高发地。Handler机制主要由四部分组成Handler负责发送和处理消息MessageQueue是消息队列Looper负责循环取消息ThreadLocal保证每个线程只有一个Looper。内存泄漏的原因要讲清楚非静态内部类Handler默认持有外部Activity的引用当Handler发送延迟消息后如果Activity已经销毁这条消息还在MessageQueue里躺着消息持有Handler引用Handler持有Activity引用Activity就无法被GC回收。解决办法有两个方向。第一把Handler改成静态内部类用WeakReference持有Activity这样即使Handler存在Activity也能被回收。第二在Activity的onDestroy里回调Handler的removeCallbacksAndMessages(null)把队列里所有属于该Handler的消息清掉。我在项目里通常是两个一起做双保险。5.3 内存泄漏检测与LeakCanary原理LeakCanary这道题面试官想看的是你知不知道它背后的检测原理。原理并不复杂LeakCanary监控Activity的onDestroy用WeakReference持有Activity同时关联一个ReferenceQueue。如果Activity正常被回收WeakReference会被加入ReferenceQueue如果过了一段时间WeakReference没有被加入ReferenceQueue说明Activity仍然被强引用持有LeakCanary就会触发一次GC再确认一次如果还是没有被回收就会主动dump堆内存并分析引用链。笔试时把这条原理完整写出来再补充一个常见泄漏场景列表比如单例持有Context、静态View引用、匿名内部类持Activity、注册的广播或回调没有解绑基本就能拿分。个人经验是LeakCanary定位到的泄漏点要尽快修带病上线越久后续排查越难因为好几个泄漏叠在一起时会互相干扰。5.4 移动端性能优化卡顿、布局与主线程移动端性能优化是这几年的重点方向热词里也有“移动端性能优化”。笔试通常会问“APP卡顿怎么优化”这道题有清晰的答题框架。先说理论基线Android每帧16.6ms超过这个时间就会掉帧用户感知为卡顿。优化方向从三个层面展开。第一布局层面减少布局层级多用ConstraintLayout避免过度嵌套合并相同类型的View第二主线程层面耗时操作搬出主线程合理使用协程或线程池避免在onDraw里做复杂计算列表滚动时避免频繁创建对象第三渲染层面利用硬件加速和缓存减少过度绘制。启动优化也是一个加分点。冷启动时间影响用户体验可以用启动助手的方案把非必要的初始化任务延后或者放到IdleHandler里空闲时执行。如果你的答案能落到具体指标比如“用Systrace分析卡顿找出耗时方法从800ms优化到300ms”这道题就是满分。5.5 网络库选型与B站移动端技术框架移动端的网络题OkHttp是绕不开的话题。为什么选择OkHttp而不是裸用HttpURLConnection核心原因是OkHttp做了连接池复用减少了TCP和TLS握手的次数内置了拦截器机制可以实现日志、重试、拦截等自定义逻辑还支持HTTP/2多路复用。拦截器是OkHttp的精华笔试题如果问请求链路ApplicationInterceptor和NetworkInterceptor的执行顺序要讲明白。请求会先经过应用拦截器再经过网络拦截器最后交给RealConnection发送响应则反向返回。利用拦截器可以做统一的签名、加解密、重试和mock。热词里还有“b站移动端技术框架有哪些”这个问题也常见。B站客户端以原生技术栈为主核心业务用Android原生和iOS原生开发部分跨端业务使用自研的跨端方案或Flutter。答题时可以带上跨端方案的对比Hybrid热更新方便但性能有限React Native性能好一点但依赖桥接Flutter性能最接近原生但包体积偏大和动态化较弱。笔试如果让你做技术选型先列业务场景再比优劣势最后给结论这个思考路径比给出一个固定答案更有说服力。6. 笔试实战经验与答题技巧6.1 时间分配选择题速战速决编程题留足时间校招笔试时间通常在120到150分钟之间题量不小。我的建议是选择题和填空题控制在总时间的三分之一以内不要把时间耗在纠结某个选项上。做不出来先跳过后面有时间再回头。简答题和场景题是拉开差距的地方答题时先写结论再写过程。比如“如何排查CPU飙高”先写“用top定位PID再jstack转储线程”再展开每一步的命令和意图。批改的人看到你的关键步骤就对了一半后面是补充细节锦上添花。编程题一定要留足至少30分钟因为要写完整可运行的代码还要考虑边界条件。6.2 编程题避坑边界条件比主流程更值钱编程题最容易丢分的地方不在主流程而在边界条件。拿前面的并发控制题举例需要考虑的边界包括tasks为空数组、limit为0或负数、limit大于tasks长度、task返回rejected Promise。把这些情况都处理掉代码质量会提升一个档次。另一个容易被忽略的是命名和结构。变量名用有意义的英文不要写a、b、temp函数划分清晰每个函数只做一件事必要的注释写清楚逻辑。笔试代码是给人看的不是只给机器运行的。如果题目要求分析时间复杂度记得在代码后面明确写出比如并发控制的空间复杂度是O(n)时间复杂度是O(totalTime / limit)。这些细节看起来小但能让你的答案在批改阶段脱颖而出。6.3 复盘方法把一道题变成一张知识网笔试结束不是终点复盘才是真正涨经验的时候。我的习惯是把每道错题当成一个知识节点往外延展。比如事件循环做错了就顺带复习宏任务微任务的完整调度、requestAnimationFrame和requestIdleCallback的区别、async/await的编译产物、Node.js的事件循环阶段。一个点带出一条链比刷十道新题效率高。我还喜欢把知识点讲给别人听。准备笔试时找一个朋友当听众把索引失效的原理、K8s调用containerd的流程、Handler内存泄漏的成因用自己的话讲一遍。讲的时候卡壳的地方就是还没真正理解的地方回到文档重新看再讲一遍。这套方法我带团队验证过很多次效果比闷头刷题好得多。最后说点个人的体会。这套B站2019年的题放到今天考点其实没过时反而因为工程化越来越重像并发控制、SSE流式推送、移动端性能优化这些点被考得更频繁。我带新人复盘这套题时发现能拿高分的往往不是刷题最多的而是那些愿意把每个答案讲明白的人。你如果正在准备笔试别急着背八股试着把每道题的答案像讲需求一样讲给别人听卡壳的地方就是你真正要补的地方。这套题值得花一个周末慢慢啃啃完之后你会发现很多面试题的背后其实是同一张知识网。