商汤前端笔试复盘:从基础考点到编程题的完整解析
1. 为什么商汤的笔试值得单独拿出来聊2018年秋招季商汤科技的前端笔试第二场我是在学校的机房完成的。当时机房里面坐着一排人屏幕上是同一套卷子周围只有键盘声和偶尔的叹气声。考完之后我在门口站了一会儿旁边几个同学互相问考得怎么样大家的第一反应都是那道题到底想考什么。回头来看商汤这场笔试之所以值得复盘不是因为它的题目特别刁钻而是因为它代表了一类公司——以AI算法为核心业务的科技公司——在招前端工程师时的真实思路。商汤不是纯互联网公司它的前端团队要面对的是算法平台、数据标注系统、模型可视化、大规模图表渲染这类场景。所以它的笔试题目不会像某些大厂那样堆砌各种偏门面试题而是更在意你对前端基础的理解深度以及拿到一个具体问题时能否快速给出靠谱的解法。这个标题里的第二场也很有意思。校招笔试通常会有多场目的是分流不同批次的候选人。第二场的题目难度往往和第一场有梯度差异也会对前一场暴露出来的问题做一些调整。把第二场拿来做分析样本其实能看到校招笔试的出题逻辑哪些是必考的核心知识点哪些是拉开差距的进阶能力哪些是用于刷人的细节陷阱。这篇文章适合两类人。一类是准备前端校招的应届生你可以拿这个复盘当一份备考提纲看看大厂笔试到底在考什么、怎么考另一类是已经工作一到三年的前端虽然你不一定再参加校招但从这套题里能看出不少基础能力的考察点对着查漏补缺也会有收获。需要说明的是我手里这份并不是官方公布的完整试卷而是基于当时参加过考试的同学回忆、各大技术社区里的讨论帖以及2018年前后前端校招笔试的普遍题型做的还原和推演。毕竟是好几年前的内容了具体题目细节会有偏差但考察的知识点和出题逻辑是可信的。这也是我写这篇复盘的核心价值——答案本身不重要重要的是理解题目背后的考察意图。2. 从题型结构看商汤前端笔试的考察逻辑先把整体结构摆出来。商汤这场笔试的时长是90分钟总的题量大致在30道左右其中选择题加填空题约20道简答题2到3道编程题2到3道。这个配置和2018年大多数互联网公司前端笔试的节奏是吻合的既要考察知识面的广度也要考察核心算法的动手能力还要通过简答题看看你对原理的理解深度。我把当时考试的大致题型分布整理成了下面这个表方便你直观感受题型题量覆盖范围建议用时选择题15道左右HTML/CSS/JS基础、浏览器机制、网络协议20分钟填空题5道左右代码输出、正则匹配、CSS属性效果10分钟简答题2到3道原理理解、方案设计、场景优化25分钟编程题2到3道数据结构、算法、JS编码能力35分钟整套卷子考下来最大的感受是基础题不送分编程题不轻松简答题最拉分。选择题和填空题覆盖的知识点非常典型几乎就是一本前端面试题库的缩影。JavaScript里闭包、原型链、作用域、this指向、Event Loop是必考CSS里盒模型、浮动、定位、flex是常客HTML里语义化标签、meta标签用法也出现过网络部分则是HTTP状态码、缓存机制、TCP三次握手这些基础中的基础。这些题本身不难但选项之间的干扰性很强如果只是知道概念但不理解原理很容易在模棱两可的选项里翻车。简答题是这套卷子比较有区分度的部分。我记得有一道题大概是让解释从输入URL到页面渲染完成浏览器经历了什么这是一个经典到不能再经典的问题但考察的是你能不能体系化地讲清楚DNS解析、TCP连接、HTTP请求、服务器响应、浏览器解析HTML构建DOM树、解析CSS构建CSSOM、执行JavaScript、合成布局、绘制页面。能把这个流程讲完整的人说明对浏览器工作机制有系统性的理解而不是零散地背了几个概念。另一道简答题涉及前端性能优化方案这类题目没有标准答案考察的是你在真实项目中到底有没有思考过这些问题。编程题的难度定位在中等偏上。不会让你去写红黑树或者复杂的动态规划但也不是随便写个循环就能糊弄过去的。考察的是两方面能力一是对JavaScript语言本身的熟练度二是对基础数据结构和算法的掌握程度。这类题目做得好不好直接决定了你笔试成绩的上限。所以商汤这场笔试的考察逻辑可以概括成三句话选择题筛掉基础不牢的人简答题筛掉只会调用API不懂原理的人编程题筛掉动手能力不过关的人。三者层层递进最终筛选出的是那些基础扎实、原理清晰、也能实际写代码的候选人。3. 高频考点的底层原理拆解基础题背后的真实意图说实话前端笔试里80%的分数都落在JavaScript、CSS、浏览器三块上。商汤这套卷子也不例外但它的题目在基础的包装下往往藏了更进一步的追问。下面我挑几个最核心的考点逐个拆给你看每个考点都会说清楚它在考什么以及为什么这么考。3.1 JavaScript 核心闭包、作用域与 this 指向JavaScript是笔试的重头戏而闭包和this指向又是JavaScript里最容易出题的部分。商汤的选择题里有一道非常经典的闭包循环输出问题请你写出下面代码的输出结果for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 100); }答案是5、5、5、5、5。如果你理解了闭包和var声明的作用域机制这个结果并不难推导。var声明的变量是函数作用域循环并不会创建新的作用域所以每个setTimeout回调函数捕获的是同一个变量i的引用。等循环结束i已经变成5了回调函数执行时打印出来的自然都是5。这道题真正的考点在于你怎么解决这个问题。如果你能在答案里顺手写出两种修复方案那理解深度这个维度就自然拉开了。方案一把var改成let利用块级作用域让每个循环迭代都有自己的绑定for (let i 0; i 5; i) { setTimeout(function() { console.log(i); }, 100); }方案二用闭包包一层立即执行函数把i作为参数传进去for (var i 0; i 5; i) { (function(j) { setTimeout(function() { console.log(j); }, 100); })(i); }笔试里考察闭包还有一个常见角度是防抖和节流的手写实现。这类题目不仅考闭包还考你对应用场景的理解。比如防抖核心思路是事件触发后设置一个定时器在定时器到期之前如果再次触发事件就清掉旧定时器重新计时。定时器就是一个闭包变量它把状态存住了。这种题目考察的是一整套思维链路你先要理解为什么需要防抖比如搜索框输入、窗口resize会频繁触发回调性能开销大然后要理解闭包如何保存定时器状态最后才是把代码写出来。this指向的问题是另一个高发区。出题人喜欢考普通函数调用、对象方法调用、构造器调用、call/apply/bind改变this指向这四种场景。基础版本是这样的var name global; var obj { name: obj, getName: function() { return this.name; } }; var fn obj.getName; console.log(fn()); console.log(obj.getName());第一行输出global因为fn被赋值后调用时它是作为一个普通函数被调用的this指向全局对象这里我按浏览器环境说Node环境里是global对象严格模式下是undefined第二行输出obj因为作为obj的方法调用时this绑定到了obj上。这个考点后面暗含的其实是JavaScript运行时的执行上下文机制——this的指向是在函数被调用时确定的不是在定义时确定的。能把这个机制讲清楚就说明你不是在死记结论。3.2 CSS 布局盒模型与经典布局实现CSS的考察核心围绕盒模型、浮动、定位、flex等布局能力展开。商汤的选择题里出现过一道关于盒模型的陷阱题一个元素的width设置为200pxpadding设置为10pxborder为2px在标准盒模型和怪异盒模型IE盒模型下这个元素实际占用的宽度分别是多少。这个问题的答案是标准盒模型下width只包含content的宽度实际占宽是20010222等于224px怪异盒模型下width已经包含了content、padding、border的总和所以实际占宽就是200px。这就是box-sizing属性存在的意义设置成border-box可以让width直接等于视觉上的总宽度这在做响应式布局时非常有用。浮动清除是另一个经典考点。有一个常见考题是父元素的高度为什么会塌陷如何清除浮动带来的影响。你可以说用空的div加clear: both但更好的方案是给父元素设置overflow: hidden或使用伪元素clearfix.clearfix::after { content: ; display: table; clear: both; }flex布局在那两年已经全面普及笔试里也会考到。比如flex: 1的含义它其实是flex-grow: 1、flex-shrink: 1、flex-basis: 0%的简写表示元素在flex容器内等比例扩展空间不够时允许缩小初始主轴尺寸为0。如果答不对这个说明你对flex的底层分配机制还停留在用了就行的阶段。让一个元素水平垂直居中也经常出现在填空题里——用flex一行就解决display: flex; align-items: center; justify-content: center;商汤在这块没有太超越常规但它的出题风格偏向实用性。它不太会考那种见都没见过的CSS属性而是考察你在真实做页面时一定会用到的知识。这其实释放了一个信号前端岗位考察的从来不是你背了多少属性而是你构建页面时能不能高效、可靠地完成。3.3 浏览器与网络从输入 URL 到页面渲染浏览器从输入URL到页面渲染的完整过程是简答题的常客也是理解前端性能优化的基础。我在考场上按这个顺序作答基本覆盖了所有得分点URL解析与DNS解析浏览器先判断输入的URL是否合法然后解析域名。DNS解析会沿着浏览器缓存、系统缓存、路由器缓存、DNS服务器逐层查找最终拿到目标服务器的IP地址。建立TCP连接拿到IP后浏览器与服务器建立TCP连接经过三次握手。这一层要说明SYN、SYNACK、ACK的作用能顺手答出HTTPS多了一次TLS握手就更好。发送HTTP请求浏览器构造HTTP请求报文包含请求行、请求头、请求体。请求头里会带上Accept、User-Agent、Cookie等关键信息。服务器响应服务器处理请求返回HTTP响应。这里状态码是考察重点2xx表示成功、301/302是重定向、304命中缓存、404是资源不存在、500是服务器内部错误。浏览器解析与渲染浏览器拿到HTML后先构建DOM树再构建CSSOM树两棵树合并成渲染树。JavaScript的加载和执行可能会阻塞这个过程所以script标签放在body底部或者使用async和defer属性。布局与绘制计算每个节点的几何位置Layout再绘制到屏幕上Paint如果页面存在动效或滚动监听还会涉及合成Composite。关于渲染过程有一个高频追问如果把script标签放在head里会发生什么。答案是它会阻塞页面渲染因为浏览器在解析到script标签时会先下载并执行JavaScript而JavaScript有可能修改DOM结构所以浏览器必须等它执行完再继续。这也是为什么我们常说脚本应该放在body底部。关于HTTP缓存笔试里出现过一道让判断304 Not Modified有什么用的题。缓存机制分强缓存和协商缓存两类可以整理成下面的表格缓存类型使用字段命中过程效果强缓存Cache-Control、Expires浏览器直接从本地读取不发请求200 (from cache)协商缓存Last-Modified/If-Modified-Since、ETag/If-None-Match浏览器带条件请求头由服务器判断是否命中304 Not Modified强缓存和协商缓存的搭配使用是前端性能优化里最基础的套路。能答出来什么时候用强缓存、什么时候用协商缓存以及为什么需要两套机制配合比单纯背字段名更有说服力。3.4 ES6 与工程化基础2018年时ES6已经全面普及Promise是考察重点。有一道简答题让对比Promise和回调函数的差异以及如何处理多个异步请求的依赖关系。最基础的场景是串行请求和并行请求串行可以用Promise链并行可以用Promise.all。如果你能补充说明Promise.all有一个请求失败怎么办或者用async/await加try/catch是更好的写法基本就能拿满分。const、let与var的对比也出现在选择题里。它们核心的差异有三个作用域var是函数作用域let和const是块级作用域、变量提升与暂时性死区var存在变量提升但let/const不会在声明之前访问会报ReferenceError、重复声明var可以重复声明let/const不行。这个知识点背后涉及的是JavaScript的编译和执行机制出题人通过这个考点来分辨你是真的理解了作用域还是只背了结论。模块化和打包机制在商汤的笔试题里也有体现。有一道填空题问ES6模块和CommonJS的差异要点是ES6模块是静态引入import语句必须在顶层编译时可做静态分析支持tree shakingCommonJS是运行时加载require是动态的。2018年恰好是webpack生态如日中天的时候考这些问题说明出题人希望候选人具备工程化思维而不是只会写原生JS。4. 编程题实战思路解题过程比最终答案更重要编程题占整套笔试时间的35分钟以上是拉分的关键。商汤这场笔试的编程题大概有三类一类是纯算法题一类是JS编码能力题还有一类是综合应用题。下面我用几道典型的题目带你过一遍完整的解题思路不要只关注最终代码思考过程才是最有价值的。4.1 数组扁平化与去重一题多解的能力展示第一道编程题很可能是数组扁平化与去重。题目一般是给定一个多维数组要求扁平化成一个一维数组同时去掉重复元素最后按升序排列。比如输入[1, [2, 3], [4, [5, 6]], 3, 6]输出[1, 2, 3, 4, 5, 6]。这道题考察三个能力很清晰递归或迭代处理嵌套结构、Set去重、数组排序。最简单粗暴的写法是用flat加Setconst arr [1, [2, 3], [4, [5, 6]], 3, 6]; const result [...new Set(arr.flat(Infinity))].sort((a, b) a - b); console.log(result);但如果你只写这个笔试官可能会觉得你在取巧。更好的做法是手写一个递归扁平化展示你对递归的理解function flatten(arr) { return arr.reduce((result, item) { if (Array.isArray(item)) { return result.concat(flatten(item)); } return result.concat(item); }, []); }然后在这个基础上做去重和排序。整个过程要注重代码的健壮性和可读性不要为了追求一行代码而牺牲可读性。面试官看编程题不是只看答案对不对还会看你的变量命名、是否处理了边界条件、代码风格是否稳定。这道题可以做个延展如果数组里还有空数组或者null你的递归能正确处理吗如果数组非常大直接递归会不会栈溢出能主动想到这些说明你在真实项目中处理过类似问题。4.2 带缓存的斐波那契从暴力到优化的完整链路另一道典型编程题是斐波那契数列的变体。普通版本是写一个函数输入n返回第n个斐波那契数。初级解法是递归function fib(n) { if (n 1) return n; return fib(n - 1) fib(n - 2); }这个解法能跑通但时间复杂度是O(2^n)n稍微大一点就会严重卡顿。于是题目一般会追问如何优化这就进入了带缓存的斐波那契——用闭包和哈希表记录已经计算过的结果function createFib() { const cache {}; return function fib(n) { if (n 1) return n; if (cache[n] ! undefined) return cache[n]; cache[n] fib(n - 1) fib(n - 2); return cache[n]; }; } const fib createFib();这个写法把时间复杂度从O(2^n)降到了O(n)而且是用闭包保存缓存状态顺带考察了闭包的实际应用。你还可以进一步给出迭代解法展示对空间复杂度的理解function fibIterative(n) { let [a, b] [0, 1]; while (n--) { [a, b] [b, a b]; } return a; }这个演进过程给面试官的信号是你不只会写一种答案还能在约束条件下选择最优方案。笔试现场时间有限我建议先写能跑的简单版本拿下基础分然后再写优化版本展示能力。不要一上来就写复杂版本导致时间不够用。4.3 手写防抖节流与柯里化JS功底的综合检验编程题里还可能出现一类工具函数题比如手写防抖、节流、柯里化。这些题目考的不是算法而是你对JavaScript闭包、this绑定、arguments/rest参数的理解。以防抖为例一个完整的实现是这样的function debounce(fn, delay) { let timer null; return function(...args) { const context this; clearTimeout(timer); timer setTimeout(() { fn.apply(context, args); }, delay); }; }注意几个细节返回的函数需要保持this指向调用者所以用了const context this加上apply参数用rest参数接收再传给原函数。能把这些细节写清楚说明你真的写过、用过防抖函数而不是背了一个模板。节流的实现思路也差不多常见的是用时间戳或者定时器实现。定时器版本的节流长这样function throttle(fn, interval) { let timer null; return function(...args) { if (timer) return; timer setTimeout(() { fn.apply(this, args); timer null; }, interval); }; }这种题目考察的更多是编码习惯和语言熟练度。如果你能在写完代码后用一句话说明防抖是等待用户停止操作后再执行节流是固定间隔内只执行一次那你就是一个有工程经验的前端而不是一个刷题机器。4.4 手写深拷贝从浅层考虑到底层机制另外一道可能出现的经典题是手写一个深拷贝函数。题目通常要求实现一个能够拷贝数组和普通对象的深拷贝部分同学可能会意识到还需要处理嵌套引用的问题。最简单的版本是JSON.parse(JSON.stringify())但它有很多坑会丢失undefined和函数属性、会把Date变成字符串、无法处理循环引用。笔试中如果你只写这个会被认为缺少经验。所以至少需要写一个递归版本function deepClone(obj, hash new WeakMap()) { if (obj null || typeof obj ! object) return obj; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (hash.has(obj)) return hash.get(obj); let cloneObj new obj.constructor(); hash.set(obj, cloneObj); for (let key in obj) { if (obj.hasOwnProperty(key)) { cloneObj[key] deepClone(obj[key], hash); } } return cloneObj; }WeakMap的用途是处理循环引用——当对象内部引用自身时递归会无限循环WeakMap可以缓存已经拷贝过的对象遇到时直接返回。能写出这个细节说明你不仅知道深拷贝的概念还理解底层的引用机制。这类题在2018年的笔试里已经算是高区分度题目能拿满分的候选人并不多。5. AI公司前端笔试的特殊变量可视化与数据展示能力商汤这类AI公司与普通互联网公司最不一样的地方在于它的前端笔试不只是考察通用前端能力还会结合自身业务特点出一些特殊考点。这批题目的比例不算高但你如果没准备现场很容易懵。5.1 数据可视化与Canvas操作做AI算法的公司运营后台里的数据展示场景非常多。训练损失曲线的展示、模型指标对比图、高维数据的降维可视化这些都需要前端通过Canvas或SVG来绘制。商汤的笔试有一道题让用Canvas绘制简单的柱状图或者考察Canvas的基础APIgetContext(2d)、fillRect、beginPath、moveTo、lineTo等。这道题看似简单但如果你之前完全没接触过Canvas写起来会非常难受。我的建议是准备前端笔试时至少要把Canvas绘制一个矩形、一个圆、一段文字的基本语法过一遍了解requestAnimationFrame的用法知道何时用Canvas、何时用SVG。就算笔试不直接考Canvas知识数据可视化相关的场景类题目也很容易在里面出现。5.2 大数据的渲染与性能优化题库里经常出现一类场景题页面上需要一次性渲染1万条甚至10万条数据你会怎么处理。这类题目在2018年就开始流行了后面几年变成了所有前端笔试的标准题型。商汤的运营平台确实会面对这样的需求比如算法日志的展示、数据集的列表。这道题至少有三个层次的答案答得越深分数越高第一层分页。一次渲染几十条通过分页器切换这是最朴素但有效的方案。第二层虚拟滚动。只渲染可视区域内的DOM节点滚动时更新渲染内容。这个方案在2018年已经有不少现成的库核心原理是外层容器固定高度内部用一个撑高的占位元素模拟滚动条长度可视区域内动态计算需要渲染哪些条目。第三层结合Canvas渲染、Web Worker做数据处理避免主线程卡顿进一步提升性能。如果你能把这三种方案都讲清楚并说明各自的适用场景这道题基本就拿到高分了。出题人不期待你在笔试现场写出一个完整的虚拟滚动组件但期待你展现出我知道大规模DOM渲染会卡知道怎么优化的架构意识。5.3 算法平台的前端架构题目除了具体的编码题商汤的笔试简答题里还有一种更具AI公司特色的考察形式给你一个算法平台的后台管理系统场景让你设计它的前端架构。比如题目可能问如果让你设计一个算法模型管理平台包含模型上传、训练状态展示、结果对比分析、权限管理等功能你会如何设计前端架构和组件划分。这种题很开放没有标准答案考察的是你的全局设计能力和业务理解力。我当时的答题思路大致是这样的先划分整体结构比如左侧菜单导航加右侧内容区的经典后台布局再拆分通用组件把模型卡片、训练进度条、图表展示抽成独立组件进行复用然后考虑状态管理用Vuex或Redux这类集中式状态库管理用户信息和模型列表最后谈到性能优化模型列表用虚拟滚动训练状态用WebSocket实时推送。这道题是我认为商汤笔试里最有区分度的一道题因为它能让你展示编程之外的能力业务拆解、架构设计、技术选型思路。这些能力在高年级前端工程师身上更被看重校招笔试就能看到这个层面说明商汤对校招生的期望是不仅能执行还能思考。6. 实战后的复盘那些容易失分的细节和备考建议到了复盘环节我把自己当时考试时踩过的坑以及后来帮别人改笔试题时发现的高频失分点集中整理一下。这些细节看上去微小但往往决定你能不能进面试。6.1 读题不仔细导致的低级失分笔试中最可惜的失分永远是没看清题目要求就开始写。比如题目里明确写了请输出所有小于n的质数有人一上来就写一个判断n是不是质数的函数题目要求用ES5写有人从头到尾用const和箭头函数题目要求数组去重后保持原来的顺序有人直接用了sort导致顺序改变。这类问题不是能力不够是应试习惯不好。我的经验是拿到题之后先圈出题目中的关键词比如输入输出格式、边界条件、是否要求排序、是否要求原地操作、是否限制使用某个API。这一步花30秒能帮你避免大量无效代码和返工。6.2 边界条件考虑不全面编程题里很多人的代码跑通了正常用例却在边界条件上翻车。最常见的边界条件是输入为空数组、输入为null或undefined、输入为负数、传入的数组长度是1。任何写代码的题目都建议在写完主逻辑后主动想想这个函数的最极端输入是什么我的代码会不会崩溃。比如写一个数组扁平化函数如果输入本身就不是数组会怎样写一个斐波那契函数n传入负数会怎样写一个深拷贝遇到循环引用会怎样这些边界问题在笔试点评中特别显眼因为正常代码大家都写得出边界处理才是区分度所在。我自己的习惯是写完代码后在代码上方用注释简要标注边界条件已考虑然后在代码中显式处理。这样考官一眼就能看到你的意识。6.3 代码风格与可读性笔试编程题阅卷过程中代码风格真的会影响到最终评分。你写出的变量名是a、b、c还是item、result、index是两种完全不同的印象。即使是在线笔试系统只跑用例不看人工评分好的命名习惯也会让代码更容易调试。更实际的一点是笔试系统有时候需要你自己处理输入输出。之前在牛客网这类在线笔试平台上输入格式经常是第一行输入一个整数n第二行输入n个整数不会给你封装好的函数参数。很多人在本地IDE里写惯了模块化代码到了笔试环境因为不会处理标准输入输出而浪费了大量时间。这个问题一定要在考前专门练习用Node.js的readline模块或者直接在浏览器里用prompt模拟至少练上两三次避免考试时慌神。6.4 时间分配失衡90分钟做30道题平均每题只有3分钟。如果你在一道选择题上磨叽了10分钟后面编程题的时间就会很紧张。我的建议是拿到卷子先花1分钟快速浏览全部题目确定哪些题是自己真正会的标出来优先做。选择题和填空题只要没有把握可以先跳最后用剩余时间回头想。编程题不要纠结于一步到位的最优解先把暴力解写出来跑通用例保底再考虑优化。我考商汤那场就是前车之鉴。有一道canvas的题我花了太多时间纠结怎么画圆角矩形结果后面一道很重要的编程题时间不够只写了一半。后来复盘时想通了笔试的核心是在有限时间里最大化得分而不是每道题都得满分。6.5 备考路线的具体建议如果你在准备前端校招笔试我推荐按这个优先级安排时间第一优先级JavaScript核心机制。闭包、作用域、this、原型链、事件循环、Promise、async/await这些是几乎所有笔试都在考的知识点。不要只看文章一定要手写代码验证自己对概念的理解。第二优先级CSS布局和浏览器机制。盒模型、flex、定位、浮动清除、BFC、事件代理、渲染流程、缓存机制。这些知识既在选择题里考也是简答题的素材库。第三优先级算法基础与编程题练习。高频题型包括数组去重、数组扁平化、深拷贝、防抖节流、柯里化、排序、去重、二分查找、字符串处理等。每类题目至少手写两到三遍标准是能在10分钟内写出能跑通的代码。第四优先级框架知识和项目经验。商汤笔试里直接考察框架的地方不多但简历里的项目一定会被面试官追问笔试之后的面试环节才是框架知识的真正考场。不要投机取巧先搞懂基础再接触框架这条路最稳妥。7. 一点个人体会把商汤2018年这场笔试从头到尾复盘一遍我自己最大的收获不是记熟了某道题的答案而是想明白了前端笔试到底在筛什么。一个候选人哪怕能流畅背出闭包的定义但让他手写一段防抖函数的代码却漏洞百出那他在实际工作中大概率写不出可维护的代码。一个候选人虽然不会背浏览器缓存的各种字段但能基于http缓存原理讲清楚静态资源更新策略那他在真实项目中一定是能解决问题的。说句实在话前端这个岗位市面上从不缺会用的人缺的是能说清楚为什么会这样的人。如果你正在准备校招笔试与其疯狂刷题求量不如把每道题背后的机制吃透。我把这场笔试的复盘分享出来也是希望你少走一点我当年走过的弯路。笔试只是第一关过了笔试后面还有面试的项目、设计、经历拷问但那都是后话了。先把这一关踩扎实一步一个脚印地往前走吧。