百度2020校招前端笔试题解析:JavaScript核心考点与避坑指南
2026/9/5 0:15:15 网站建设 项目流程

百度2020校招Web前端工程师笔试卷(第三批),说实话,拿到手第一感觉是:稳。没有太多偏题怪题,绝大多数考点都踩在前端校招的常规主线上:JavaScript、浏览器、CSS、框架和工程化。但如果你因此小看它,很容易在几个细节上翻车。这份试卷适合两类人:一类是准备秋招的应届生,想快速摸清百度这类大厂筛人的底层逻辑;另一类是有一两年工作经验但没正经参加过笔试的前端,用来检验自己的基础是否扎实。我在刷这份卷子的时候,最大的感受是:它考的其实不是你会不会某个API,而是你在真实开发里有没有养成把问题想透的习惯。后面我会直接拆考点、讲题目,再分享一些踩坑经验。

1. 从一份百度校招笔试卷,反推Web前端必考点

1.1 第三批试卷到底在筛什么人

校招笔试有一个整体逻辑:第一批、第二批通常是海量筛选,题目会偏向知识面覆盖,看你能不能把前端主干知识串起来;到了第三批,题目风格往往会更收敛,更看重基本功的扎实程度。百度2020年这份第三批试卷给我最强烈的信号是:它不想靠生僻题难倒你,而是想看你面对常规问题时能不能做到严谨、完整、有边界感。

所谓“边界感”,举个例子:手写一个防抖函数,很多人能写出来,但面试官会继续问“如果事件在等待时间内又被触发了无数次,你的定时器会不会累积”,或者“如果这个函数有返回值,该怎么处理”。试卷不会直接让你说这些,但代码题的评分点里一定有这些隐藏条件。第三批试卷就是在通过这种“常规题+隐藏条件”的方式,筛掉只会背代码、不会想为什么的候选人。

同时,试卷也承担着“岗位匹配”的作用。百度当年的前端团队有大量搜索生态、信息流和智能小程序业务,这些业务对首屏性能、缓存策略、交互细节要求很高,所以浏览器原理和网络相关内容必然占大头。你去看这份卷子的考点分布,会发现它非常贴近“真实业务里每天都会遇到的问题”,而不是纯理论的算法题。

1.2 高频考点分布:JS是绝对大头

把这份卷子按知识点归类,占分最高的无疑是JavaScript,粗略估计在四成以上。其次是浏览器与网络相关,大概三成,剩下的是CSS布局、框架思想、工程化概念和少量数据结构题。这和很多同学以为的“校招笔试等于算法题”完全不同,前端岗位的笔试题更偏向语言特性、运行时机制和工程实践。

JavaScript部分几乎绕不开这几个方向:变量提升与作用域、闭包与立即执行函数、原型链与继承、Event Loop与异步顺序、手写数组方法或工具函数。这些题目经常交叉出现,比如“for循环中用var声明变量,setTimeout里输出i”就是作用域和异步的综合题,一道题就能看出你对执行栈、任务队列、词法环境的理解。

浏览器与网络部分则集中在HTTP缓存、输入URL到页面展示的完整过程、事件循环在浏览器里的具体表现、重排和重绘的区别。这些知识点有一个共同特点:你在浏览器开发者工具里都能直接观察到,所以比纯粹背理论更容易复核。笔试时如果答不全,说明平时调试页面时没有带着问题去看Network和Performance面板。

1.3 为什么大厂爱考这些基础题

很多人不理解,工作里明明都用框架和组件库,为什么笔试还要手写防抖、节流、深拷贝、发布订阅。其实大厂要的不是你重复造轮子,而是想确认三件事。

第一,你有没有真正理解JavaScript这门语言的运行机制。框架层帮你做了很多事,但遇到线上问题时,最终要回到原生JS层面去定位。比如页面上有个定时器导致内存泄漏,如果你不理解闭包和引用关系,根本不知道从哪儿查。第二,你具不具备写可维护代码的素养。手写工具函数看似简单,但函数命名、参数默认值、异常处理、边界判断,每一条都反映你平时写代码的习惯。第三,你能否在团队里做技术输出。百度内部有大量自研组件和工具链,成员需要能读源码、改源码,甚至给团队沉淀公共方法,这些都建立在基础扎实的前提之上。

所以我建议准备笔试时,不要只背面经,而是把每个高频考点往“业务场景”上靠一靠,问自己:这个知识点解决了什么真实问题?这样一来,即使笔试碰到没见过的题,你也能从原理出发给出合理答案。

2. 拿到试卷别急着写,先做时间预算

2.1 常见题型和分值占比

百度2020校招Web前端工程师笔试卷(第三批)整体结构可以分成四类:单选题、多选题、简答题和编程题。单选题、多选题考察知识面的广度和准确度,简答题考察对原理的表述能力,编程题则直接看代码功底。

根据我个人的做题经验,可以用下面这个表来估算一次笔试的时间分布:

题型大致占比建议用时答题目标
单选题20%-30%20-30分钟正确率优先,不要恋战
多选题10%-20%10-20分钟拿不准就选最确定项
简答题20%-30%30-40分钟写清关键词和逻辑链
编程题30%-40%60分钟以上先跑通,再补边界

这只是一个大致的配比,不同批次可能上下浮动。但你拿到卷子后,第一件事一定是扫一遍所有题目,标出哪些题是你一眼就会的、哪些是你需要算一下的、哪些是完全没思路的。这个动作花不了五分钟,却能帮你避免在前面的选择题上耗掉太多时间,最后编程题只能仓促交卷。

2.2 我的答题顺序和优先级

我自己做这类卷子的习惯是:先做编程题,再做简答题,最后做选择填空。原因很简单,编程题的分值高,而且需要大脑在清醒状态下处理作用域、异步、边界条件等问题;如果先把选择填空做完,脑子里塞满了碎片化概念,再写代码时反而容易乱。

编程题内部也有优先级。先挑最短、最熟悉的题写,比如手写一个防抖函数、实现一个深拷贝,这类题代码量小、得分点明确,写完能立刻给你信心。然后再去碰需要更多思考的题,比如实现一个带并发限制的异步调度器、解决循环引用问题。

简答题不建议写太多废话。阅卷时通常按关键词给分,你可以先想清楚要答哪几个点,再用“一句话结论+过程解释”的方式写。比如问“为什么虚拟DOM能提升性能”,最好的回答是先说“虚拟DOM减少了真实DOM操作次数”,再解释diff算法如何复用节点,而不要去背一整段源码分析。

2.3 时间不够时的取舍策略

校招笔试时间总是紧的,尤其是编程题需要调试,更容易超时。如果时间不够,我建议按“代码题 > 简答题 > 多选 > 单选”的优先级取舍。因为编程题可以部分得分,简答题写了关键词就有一半分,而选择题蒙对的概率不可控。

我当时做这份卷子时,最后剩十五分钟还有一道简答题没写完。我没有再纠结细节,而是马上用几行伪代码把自己的思路写出来,再把关键术语比如“强缓存”“协商缓存”“Last-Modified”“ETag”全部列上去。最后那题拿到了一半以上的分,比硬憋一句完美但晚交的答案划算得多。

还有一点:不要因为某道题不会就一直停在原地。笔试系统一般有倒计时,你在一道题上耗十分钟,相当于后面编程题少了十分钟。遇到卡壳的题目先做标记,做完其他题目再回来,这种策略在时间紧张时非常有效。

3. 核心知识点逐项拆解:从原理到手写

3.1 JavaScript:闭包、作用域、异步

JavaScript在这份卷子里可以说是“无处不在”。闭包、作用域、异步三兄弟,几乎每年必考。闭包的本质是“函数在定义时捕获了外层作用域”,因此外层函数执行完,内部函数仍然能访问到外层变量。笔试常考的是“用闭包实现变量私有化”或者“给循环里的异步回调传值”。

作用域方面,高频考点是var、let、const三者的区别。var会变量提升,还能重复声明;let有块级作用域但没有提升时的初始化,所以有暂时性死区;const和let类似但变量引用不可变。别小看这个基础问题,很多人知道结论,但一放到for循环和setTimeout的组合题里就翻车。

异步部分更看重Event Loop。你需要清楚宏任务和微任务的执行顺序,比如Promise和setTimeout混在一起时,谁先执行。笔试不会只问你概念,很可能会给一段代码让你写出输出顺序,比如:

console.log('start'); setTimeout(() => { console.log('timeout'); }, 0); Promise.resolve().then(() => { console.log('promise'); }); console.log('end');

这个输出顺序是:start、end、promise、timeout。关键在于同步代码先执行完,微任务队列清空后才会执行宏任务。如果你能把这个流程在脑子里完整跑一遍,类似题目就不会再丢分。

3.2 浏览器与网络:缓存、渲染、事件循环

浏览器和网络部分,最值得花时间的是HTTP缓存。强缓存和协商缓存是高频考点。强缓存直接用Cache-Control和Expires,命中后不会发请求;协商缓存需要向服务器确认,用Last-Modified/If-Modified-Since或ETag/If-None-Match。

笔试可能会给你一个业务场景:静态资源需要秒开,应该怎么设缓存?答案思路是:给静态资源设置长时间强缓存,比如Cache-Control: max-age=31536000;文件名带上hash,内容变了文件名就变,自然能突破缓存。这个方案涉及两个知识点,一个是缓存优先级,一个是前端工程化里的文件指纹,能写全说明你有真实项目经验。

渲染流程方面,要理解从HTML字符串到像素的完整过程:解析HTML生成DOM树,解析CSS生成CSSOM,合成渲染树,然后布局、绘制、合成。笔试里常考的方式是:“重排和重绘有什么区别?哪些操作会触发重排?”回答要举实际例子,比如修改宽度、读取offsetHeight、改变字体大小都会触发重排;修改color或background则只触发重绘。

事件循环在浏览器里还涉及“宏任务”里的setTimeout、setInterval、I/O,以及“微任务”里的Promise、MutationObserver。多轮笔试总结下来,只要你画出“同步代码 -> 微任务 -> 宏任务 -> 再微任务”的循环图,这类题基本稳了。

3.3 CSS:布局、层叠上下文、移动端适配

CSS部分看着占比不高,但常常在简答题里出现。第一梯度是布局题,比如“实现一个两栏布局,左边固定200px,右边自适应”。答案有很多种:float、flex、grid。笔试阅卷时不一定要求你写出所有方案,但最好能写两种以上,并说明各自的优缺点。

flex布局是最好用的答案。给容器设置display: flex;左边固定宽度flex: 0 0 200px;右边flex: 1。这样右边会吃掉剩余空间。再深入一点可以写grid布局:grid-template-columns: 200px 1fr。写到这里,阅卷人就知道你不只是用过flex,还了解新布局方式。

层叠上下文也是容易被忽略的考点。z-index不是随便设就能生效的,只有定位元素或flex子项等特定条件才能创建层叠上下文。面试时经常有人说“我把z-index设为999999怎么还是被盖住”,很可能是父级创建了层叠上下文,导致子元素的z-index只在父级内部比较。笔试题如果让你解释某个元素的层级,你要注意检查所有祖先的transform、opacity、filter等属性。

移动端适配常考rem和viewport。以前我们常用rem加媒体查询,现在更多是用vw/vh或者基于设计稿的flexible方案。理解思路比死记代码更重要:无论哪种方案,目标都是让同一套设计稿在不同屏宽下等比展示。

3.4 框架与工程化:组件通信和构建思路

2020年这个时间点,Vue和React已经占据主流,笔试试卷里也会出现框架相关题型。虽然不会要求你默写某个组件,但会经常问你“父子组件通信怎么做”“兄弟组件怎么共享状态”“Vue和React的diff算法有什么区别”。

组件通信这类题,回答时要分层:父传子用props属性,子传父用自定义事件或回调函数;跨层级或兄弟组件,可以用全局状态管理,比如Vuex或Redux;如果项目很小,也可以直接用EventBus或Context。关键不是背方案,而是有场景判断力:小范围通信别引入Redux,否则维护成本反而高。

工程化方面,Webpack是必考项。你需要知道entry、output、loader、plugin分别干什么,而不是只听说“Webpack可以打包”。常见的loader比如babel-loader能把ES6+转成ES5,css-loader和style-loader能处理CSS。plugin里的HtmlWebpackPlugin能自动生成HTML并引入打包后的脚本。笔试题可能让你说说loader和plugin的区别,简单一句话:loader是处理模块的转换器,plugin是作用于整个构建流程的扩展器。

还有模块化。CommonJS和ES Module的区别、import和require的差异,都是选择题常客。答题要点:CommonJS是同步加载,常用于Node;ES Module是编译时静态分析,支持tree shaking。这些知识点在一份笔试试卷里出现的频率非常高。

4. 四道高频真题的解题思路与踩坑实录

4.1 循环闭包输出题:别被var骗了

这类题几乎每份前端笔试卷里都有,百度这份也不例外。常见版本是这样的:

for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 100); }

很多人第一反应是输出0、1、2、3、4,但实际输出的是5、5、5、5、5。原因是var声明的i是函数作用域,循环结束后i已经变成5,五个回调函数共享同一个i。

解决办法有三种:把var改成let;用立即执行函数包一层并传入i;用setTimeout的第三个参数传i:

for (var i = 0; i < 5; i++) { setTimeout(function(j) { console.log(j); }, 100, i); }

笔试题通常不会只让你“写出输出”,而是会追问“怎么改才能正确输出”。所以每种方案最好都准备一遍,并且明白它们各自的原理。let方案依赖块级作用域,立即执行函数依赖闭包,第三个参数依赖setTimeout内部透传参数,三种思路体现的是不同的语言特性。

4.2 手写深拷贝:边界条件才是加分项

手写深拷贝是编程题里的硬骨头。基本版很容易:遍历对象,判断属性类型,递归复制。但一份能上笔试卷子的深拷贝,至少得处理数组、Date、RegExp、Map、Set、循环引用。

下面这个版本是我遇到比较多的思路:

function deepClone(target, map = new WeakMap()) { if (target === null || typeof target !== 'object') { return target; } if (map.has(target)) { return map.get(target); } const result = Array.isArray(target) ? [] : {}; map.set(target, result); if (Object.prototype.toString.call(target) === '[object Date]') { return new Date(target); } if (Object.prototype.toString.call(target) === '[object RegExp]') { return new RegExp(target.source, target.flags); } Object.keys(target).forEach(key => { result[key] = deepClone(target[key], map); }); return result; }

这段代码里,WeakMap是关键。它既能解决循环引用,又不会因为拷贝过程产生内存泄漏。如果不处理循环引用,直接递归拷贝一个互相引用的对象会导致栈溢出。

笔试时还要注意几个小细节:Symbol属性要不要拷贝?普通Object.keys是拿不到的,可以用Reflect.ownKeys。函数要怎么办?函数一般直接复用引用即可,不需要深拷贝。这些边界条件即使没有完全写出来,你在代码旁注释一句“这里我按以下方式处理”,也能让阅卷人看到你有边界意识。

4.3 HTTP缓存策略:一道场景题串起所有字段

校招笔试很少让你默写缓存字段,更常见的是给你一个场景:“你的页面引用了app.js,每次发布后用户还是加载旧文件,怎么解决?”这道题本质上问的是HTTP缓存。

标准思路分两层。第一层是协商缓存:服务器返回的响应头带Last-Modified或ETag,浏览器下次请求时带上If-Modified-Since或If-None-Match,如果文件没变,服务器返回304,浏览器使用本地缓存。第二层是强缓存:加Cache-Control: max-age=86400,在max-age过期前,浏览器根本不发请求。

但生产环境里,针对带hash的文件,我们通常会设置一个很长的强缓存时间,比如一年。文件内容变了,构建工具会生成新的hash文件名,浏览器请求的是新URL,自然绕过缓存。如果文件名不变,就用协商缓存来确保更新。这就是现代前端发布和缓存配合的通用策略。

答题时如果能写出“文件名指纹”这个概念,分数会明显不一样。它说明你真正上线过项目,知道怎么解决发版之后用户缓存不刷新的问题,而不是只会背请求头。

4.4 手写发布订阅:实现简单,细节决定成败

发布订阅模式在前端里无处不在,Vue的$emit和$on就是基于这个思想。笔试现场写一个EventEmitter,很多同学能写出来但拿不满分,问题通常出在“取消订阅”和“只订阅一次”两个方法上。

一个常规实现:

class EventEmitter { constructor() { this.events = new Map(); } on(name, fn) { if (!this.events.has(name)) { this.events.set(name, []); } this.events.get(name).push(fn); } emit(name, ...args) { if (!this.events.has(name)) return; for (const fn of this.events.get(name)) { fn(...args); } } off(name, fn) { if (!this.events.has(name)) return; const list = this.events.get(name).filter(item => item !== fn); this.events.set(name, list); } once(name, fn) { const wrapper = (...args) => { fn(...args); this.off(name, wrapper); }; this.on(name, wrapper); } }

这个类很短,但亮点在两个地方:off要能精确删除某个监听函数,而不是把整个事件清空;once要用一个包装函数,调用完原函数后自动卸载自己。如果笔试题还要求支持“给同一个函数绑定多次后同一函数只移除一次”,还需要额外设计。这些细节恰恰是代码能力的体现。

5. 常见失分点与查漏补缺技巧

5.1 代码题“看起来对,一跑就错”的四个原因

我刷这份试卷时,最常犯的错误集中在四个地方。第一个是变量类型判断不够严谨,比如typeof null是object,直接把null漏进去了;第二个是数组和普通对象没有分开处理,导致数组被深拷贝成对象;第三个是递归出口没有写,栈溢出;第四个是循环引用没有处理,直接报错。

其中循环引用是最容易被忽略的。笔试代码题往往只给了简单示例对象,很多人写完基本版就以为自己通关了。但只要阅卷人补一个obj.self = obj的场景,代码就崩了。所以每次写完手写代码,我都习惯追问自己三个问题:入参为空会怎样?两次调用会不会互相影响?对象里有引用自身的属性会怎样?这三个问题能帮你抓住大多数隐藏bug。

还有一类失分是“细节没写完整但代码风格不错”——比如没有用const/let,全部用var;没有给函数写默认值;没有考虑对象为空对象时该怎么办。这些不会直接判错,但会影响印象分。建议笔试时尽量用ES6+写法,至少说明你平时跟得上规范更新。

5.2 简答题怎么答才能拿到踩分点

校招笔试的简答题不是写小作文,而是踩点给分。我踩过的坑是:洋洋洒洒写了一大段,发现核心关键词一个都没写全。

拿“说一下浏览器输入URL到页面展示的过程”来举例,这道题基础分就在几个关键节点上:DNS解析 -> TCP连接 -> 发送HTTP请求 -> 服务器返回响应 -> 浏览器解析渲染 -> 连接关闭。如果你能再补充“构建DOM树、构建CSSOM、生成渲染树、布局、绘制”,分数会更高。但很多同学会直接从“浏览器发起请求”开始写,漏掉DNS解析和TCP建连,白白丢分。

我的建议是:简答题先列点再解释。哪怕你只列几个名词,也会被看作“有思路”。遇到不会的题,也别空着,用你已有的知识推导一下,写一些有逻辑的推测,往往能拿到部分分数。

当然,简答题还要避免写太多口语化表达。平时我们聊天会说“浏览器就会去那个服务器拿文件”,但笔试时最好改成“浏览器向服务器发起HTTP GET请求,获取HTML文档”。既显得专业,又容易让阅卷人把关键词对应上。

5.3 我整理的一道自查清单

现在市面上能刷的真题很多,但很多同学是在“背题”而不是“补漏”。如果你准备过校招笔试,可以拿着这份清单自查一遍:

  • 能不能独立手写防抖、节流、深拷贝、发布订阅?
  • 能不能完整说出浏览器从发起请求到页面渲染的每一步?
  • 能不能区分强缓存和协商缓存,并说明Cache-Control与Expires的优先级?
  • 能不能在Node环境里解释清楚Event Loop和浏览器Event Loop的差异?
  • 能不能说明盒模型、BFC、层叠上下文分别解决什么问题?
  • 能不能用flex和grid各实现一次经典的垂直水平居中?
  • 能不能说清webpack中loader和plugin的区别,并分别举一个例子?
  • 能不能解释Virtual DOM的diff过程为什么比直接操作DOM快,以及它一定快吗?
  • 能不能解释常见内存泄漏场景,比如闭包引用、全局变量、定时器未清理?
  • 能不能手写一个Promise.all,或者说明它的实现思路?

上面十项,能秒答七项以上,基础基本过关;回答不上来的,就是你应该优先补的板块。不要贪多,把每一项都拆成一个一个小的可验证练习,比看十篇面经有用得多。

最后再分享一个我自己的习惯:每次刷完一份笔试卷,我会把错题整理成一份“错题原因”清单,区分是“知识盲区”还是“粗心大意”。知识盲区就专项突破,粗心大意就靠训练检查习惯。刷题不追求数量,而追求每一道题都能帮你补上一个具体短板。这份百度第三批笔试卷,我在刷第三遍的时候已经很少再错,但每一次重做都能发现一些新的表达方式,这也算是它值得反复研究的价值所在。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询