ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

百度2020校招前端笔试题解析:JavaScript核心考点与避坑指南

百度2020校招前端笔试题解析:JavaScript核心考点与避坑指南 百度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-age31536000文件名带上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转成ES5css-loader和style-loader能处理CSS。plugin里的HtmlWebpackPlugin能自动生成HTML并引入打包后的脚本。笔试题可能让你说说loader和plugin的区别简单一句话loader是处理模块的转换器plugin是作用于整个构建流程的扩展器。还有模块化。CommonJS和ES Module的区别、import和require的差异都是选择题常客。答题要点CommonJS是同步加载常用于NodeES 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的第三个参数传ifor (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-age86400在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或者说明它的实现思路上面十项能秒答七项以上基础基本过关回答不上来的就是你应该优先补的板块。不要贪多把每一项都拆成一个一个小的可验证练习比看十篇面经有用得多。最后再分享一个我自己的习惯每次刷完一份笔试卷我会把错题整理成一份“错题原因”清单区分是“知识盲区”还是“粗心大意”。知识盲区就专项突破粗心大意就靠训练检查习惯。刷题不追求数量而追求每一道题都能帮你补上一个具体短板。这份百度第三批笔试卷我在刷第三遍的时候已经很少再错但每一次重做都能发现一些新的表达方式这也算是它值得反复研究的价值所在。
返回列表