百度前端校招笔试题解析:从JavaScript基础到浏览器原理

百度前端校招笔试题解析:从JavaScript基础到浏览器原理 先说明一下我是2017年参加的秋招当时考的批次和这份第三批的题目有些重合所以看到这份“百度2018校招Web前端工程师笔试卷第三批”时很多题我都有印象。每年校招季都有人问我要真题、要答案我一般不太建议直接背答案因为百度这几年的笔试题有个特点——题量不大但每一道题都留了扩展空间面试官真正想看的不是你能不能写出标准解而是你面对一个开放问题时的思考路径。这篇文章我不打算把整张卷子的每一道题都复述一遍毕竟网上随手能搜到原题我只挑那些有代表性、有深度、值得反复咀嚼的题目来拆。对于那些正准备投简历的同学与其说这是一份“答案解析”不如说这是帮你少走弯路的“踩坑复盘”。1. 这份卷子的整体布局从题型看百度的用人标准拿到一张笔试卷先别急着做题先看它考什么、不考什么。这比做对一道题重要得多。1.1 题量不大但覆盖面很典型2018年这批前端笔试卷总体题型分为几块基础知识选择题、代码阅读题、手写代码题、简答/设计题。题量大概在二十道左右考试时间我记得是90分钟。乍一看时间还算充裕但如果手写代码题卡住了时间就非常紧张。从知识点分布来看几个大类的权重我大概估算一下知识模块大概占比常见出题形式HTML/CSS20%左右选择题、阅读题JavaScript核心35%-40%选择题、手写代码题浏览器与网络15%左右简答题、选择题框架/工程化10%左右简答题、设计题逻辑与算法10%-15%手写代码题、算法题这个分布说明一个问题百度校招前端笔试JavaScript的比重远高于HTML/CSS而且考点集中在语言本身的机制上——闭包、作用域、this指向、事件循环、原型链这些几乎每年必考。1.2 为什么重点考JS而不是框架很多同学复习的时候喜欢死磕Vue或React的源码结果笔试一考闭包反而懵了。我在之前带过的实习生里见过不少这种情况。这里有个逻辑校招笔试筛的是“底子”不是筛“项目经验”。框架更新这么快今天Vue3、明天React 18如果靠背框架API来筛选那招进来的人可能过两年就废了。JS语言机制、浏览器原理、网络基础这些十年都不会大变这些才是值得用一整套笔试题去测的东西。我当时复习时也犯过类似的错花了很多时间看框架源码结果笔试遇到一道简单的“用原生JS实现数组去重”我因为平时都用Set一行搞定反而卡住了。所以这份卷子最大的价值是给你提个醒校招前把原生JS的基本功打牢比什么都重要。2. HTML/CSS部分真题复盘基础题也可以挖出深坑这份卷子里的HTML/CSS题表面上看考的都是“基础”但每一题都有一两个坑。我挑了三个有代表性的说。2.1 盒模型的计算看似送分实则送命题目大概是给出一个元素的width、padding、border考box-sizing在不同取值下实际渲染宽度是多少。这是前端笔试最经典的送分题但拿满分的人不多。核心考点就两个box-sizing: content-box默认值下width只包含内容区实际占位宽度 width padding-left padding-right border-left border-right。box-sizing: border-box下width包含了padding和border实际占位宽度就是width。我当时做题时特别留意了题目里有没有写box-sizing因为很多题目默认不给这个属性然后一大堆人默认按border-box算直接就错了。如果你把这两条规则写清楚再标注“未声明box-sizing时默认是content-box”阅卷人一眼就看出你基础扎实。2.2 层叠上下文z-index不是全局的卷子里有一道阅读题给了几个嵌套的div设置了不同的z-index和position问最终哪个元素在最上面。这类题考的是层叠上下文stacking context。常见的坑有这几个z-index只有在定位元素position不为static且设置了z-index值时才生效。如果父元素创建了层叠上下文子元素的z-index只在父级上下文内部比较不能跨层级和外面的元素比大小。transform、opacity、filter等属性也会创建层叠上下文即使position是static。我记得这道题给出的元素中有一个设置了opacity: 0.5很多人会忽略它也能创建层叠上下文结果排序就错了。这类题目是真正考“经验”的——没有实际遇到过层叠上下文问题的人很容易按直觉去比z-index数值。2.3 两栏布局的N种写法手写代码题或者简答题里出现过“左边固定宽度200px右边自适应”的布局题。这题本身不难但给了你发挥空间能看出你平时积累的布局方案多不多。我能想到的解决方案有这些float margin-leftposition绝对定位flex布局grid布局table-cell如果是面试我一般会建议至少说出三种并且说明各自的优缺点。笔试卷上如果只写一种虽然也对但没法展示你的知识广度。我自己一般先写flex——代码最简洁兼容性对于校招场景来说也够用然后会补一个float的经典方案说明自己了解布局的历史演进过程。3. JavaScript核心原理题这份卷子的重头戏JavaScript部分的题目是整张卷子的核心也是拉开分差的地方。我按知识点拆开说。3.1 闭包几乎所有手写题都绕不开卷子里有一道题大概是for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); }问输出什么以及如何改成输出0、1、2、3、4。这道题已经老掉牙了但每年都考每次都有很多人掉坑。它考的核心就是var没有块级作用域setTimeout里的回调函数执行时循环已经结束了i变成了5所以五次输出都是5。要改成输出0到4有这几种方式用let声名i形成块级作用域。用IIFE立即执行函数包裹把i作为参数传进去。在setTimeout外面套一层函数利用函数参数传值。我当时选择用let改写顺便在注释里补充了IIFE的写法因为这种题多写一种方案阅卷人能看出你不只是背了答案真的理解闭包和函数作用域。有些同学可能觉得let是ES6语法担心浏览器兼容性但在2018年的笔试环境下let已经被广泛支持了写上没毛病。3.2 this指向和call/apply/bind的区别卷子里有几道选择题和阅读题考this的指向问题比如普通函数调用时this指向什么对象方法调用时this指向什么箭头函数的this和普通函数有什么区别如何改变this指向这种题难的不是记忆而是能不能熟练运用。如果问你call、apply、bind三者的区别标准答案大概是call和apply都是立即执行区别是传参方式不同call是一个个传apply是传数组。bind不立即执行而是返回一个新函数新函数的this被永久绑定到指定对象。但真正理解的回答应该是这三者都是为了解决function调用的this丢失问题。实际开发里最常用的场景是setTimeout里要访问外部的this或者把对象的方法拆出来单独调用时那个方法里的this会变成undefined或window。我还见过笔试题目这样考手写一个简单的bind函数。思路就是用call或apply像这样Function.prototype.myBind function(context) { var fn this; var args Array.prototype.slice.call(arguments, 1); return function() { var bindArgs Array.prototype.slice.call(arguments); return fn.apply(context, args.concat(bindArgs)); }; };这类题目考的是你对this和函数调用的理解深度不是死记硬背API。3.3 事件循环Event Loop从输出顺序看异步机制2018年的笔试卷已经有了不少关于事件循环的题目比如让判断一段包含setTimeout、Promise、async/await的代码的输出顺序。JavaScript的事件循环机制核心是先执行当前宏任务里的同步代码执行所有微任务Promise的then回调、async函数里的await之后的代码、MutationObserver回调等然后执行下一个宏任务setTimeout、setInterval、I/O等数组里有一个经典的题目答案setTimeout(fn, 0)并不是立即执行而是要等当前任务队列清空后才执行。而Promise的resolve会立刻把then回调放到微任务队列所以Promise的输出可能在setTimeout之前。我见过很多人在这个点翻车因为async函数对于初学者来说很容易搞混await之后的代码什么时候执行。我给一个简单的记忆方法await后面的代码相当于被放进了then回调里是微任务await表达式本身会立刻执行如果它是Promise则注册then回调如果它是一个普通值也会被包装成Promise。3.4 数组去重和排序看似简单展开很多手写代码题里有一道非常经典的“数组去重”。这道题可深可浅一展开全是知识点。最简单的是ES6的[...new Set(arr)]一行搞定。但如果笔试卷要求不能用Set你就得手写。我一般会建议按这个思路演进最简单的双重循环逐个比较。优化一点用一个hash对象存已出现的值注意类型区分。再优化用indexOf判断是否已存在。更进一步考虑有序数组的特殊场景用相邻比较。性能最优Set或Map。如果题目还要求排序那就要注意去重和排序的综合实现。比如先用filter和indexOf去重再调用sort排序但如果数组里有字符串和数字混合排序结果可能和预期不一致因为sort默认按字符串的Unicode编码排序。我当时写这道题时特意在代码注释里标注了“如果要排序建议先sort再去重”因为对于有序数组去重可以做到O(n)的时间复杂度。这种细节虽然不影响AC但能让阅卷人看到你思考过边界问题和复杂度优化。3.5 原型链和面向对象试卷里还有一道题考原型链我印象很深因为那道题的代码嵌套了好几层涉及构造函数、原型、实例之间的指向关系。核心要记住这几个点构造函数.prototype指向原型对象。实例的__proto__指向构造函数的prototype。所有对象都继承自Object.prototype。函数也是对象所以函数也有__proto__。这种题最怕的是把prototype和__proto__搞混。我教别人一个记忆法prototype是构造函数才有的属性__proto__是每个对象都有的属性除了用Object.create(null)创建的后者是实例指向原型的桥梁。还有一道题是关于ES6的class和ES5的构造函数写法之间的转换这考的是JS面向对象设计的本质。class其实只是语法糖它底层的继承机制依然是原型链。我当时在答题时把class继承的写法翻译成了ES5的原型链写法展示了自己理解class的本质。4. 网络与浏览器简答题里那些必须答全的知识点前端笔试一般还会考网络、浏览器相关的题第三批的卷子里在这块也出了不少题目。好好复习这些知识对校招很有价值。4.1 HTTP状态码不只是记住数字有一道题考HTTP状态码的含义比如200、301、302、304、401、403、404、500、502、503。很多人能记住几个常见的但容易混的是301和302还有304。我总结一个记法301是永久重定向浏览器会缓存新的地址以后直接访问新地址。302是临时重定向每次还是先访问旧地址再跳转。304是Not Modified表示资源未修改可以继续用本地缓存不需要重新下载。关于304很多人以为304是“服务器没有返回内容”其实服务器返回了状态码304和响应头信息只是没有响应体。这是协商缓存的机制浏览器发了请求带If-Modified-Since或If-None-Match头服务器判断内容没变就直接返回304告诉浏览器用缓存。4.2 跨域必考的大题跨域几乎是所有前端笔试卷里必考的百度的这份也不例外。问的方式可能是什么是同源策略如何实现跨域我推荐按这个逻辑答题先解释为什么要有同源策略为了安全防止恶意网站读取另一个网站的Cookie或数据。指出跨域指的是协议、域名、端口任一不同。列举跨域的解决方案JSONP利用script标签不受同源限制的特点服务端返回一段JS代码调用前端定义好的回调函数。CORS后端设置响应头Access-Control-Allow-Origin。通过postMessage进行跨窗口通信。利用服务器代理转发请求。如果笔试只让你手写JSONP的实现那核心代码也不复杂function jsonp(url, callbackName, success) { var script document.createElement(script); script.src url ?callback callbackName; window[callbackName] function(data) { success(data); document.body.removeChild(script); }; document.body.appendChild(script); }要点是先定义一个全局函数再动态插入script标签服务端返回的JS会调用这个全局函数并传入数据。JSONP只能支持GET请求因为它是通过script标签加载的这一点一定要提。4.3 浏览器缓存机制强缓存和协商缓存浏览器缓存是笔试里另一个高频考点。核心要能说清楚两种缓存缓存类型判断依据特点强缓存Expires、Cache-Control命中后不发请求直接用本地缓存协商缓存Last-Modified/If-Modified-Since、ETag/If-None-Match发出请求服务器返回304或200Cache-Control有几种常见取值no-cache虽然名字叫no-cache但其实每次还是会发请求验证一下强缓存是不发请求直接用的、no-store真正的完全不缓存、max-age缓存多少秒、private/public。在笔试答题时能画出一个请求和响应的时间线来说明这个过程是最直白的答案。我在答题时是画了一个简单的表格来描述整个流程因为纯文字描述比较绕。5. 开放题和算法题怎么答才有区分度百度的笔试卷一般还会有一两道开放性的简答题和一两道算法题这部分是拉分的关键。5.1 从一个URL输入到页面渲染的过程这是超级经典的开放题考的是对整个Web运作流程的理解深度。完整的答案应该包含这么几个阶段DNS解析把域名解析成IP地址。TCP连接三次握手建立连接如果是HTTPS还要加上TLS握手。发送HTTP请求携带请求头、请求体服务器处理并返回响应。浏览器收到HTML开始解析。解析HTML生成DOM树解析CSS生成CSSOM树。DOM树和CSSOM树合并成渲染树Render Tree。布局Layout计算每个节点的位置和尺寸。绘制Paint把内容画到屏幕上。这题的深度就藏在每一步的细节里。比如DNS解析的过程你知不知道有DNS缓存、如何递归和迭代查询TCP三次握手为什么不是两次CSS是否会阻塞渲染script标签放在head里会阻塞HTML解析async和defer有什么区别虽然这一题没有标准答案但能写出“渲染树”和“布局”这种非前端同学不太知道的词就能让阅卷人看出你确实深入学过浏览器原理。5.2 手写Promise或者async相关的算法题2018年的笔试卷已经开始涉及Promise的手写实现或者基于Promise的异步流程控制题。我记得有一道题是类似于“限制并发请求数量”的题目——给一个URL列表限制最多同时发起3个请求全部完成后输出结果。这道题其实是一道偏向实际工程的算法题。解法思路是维护一个执行队列每次最多执行3个任务一个任务完成后从队列里取下一个任务继续执行。我当时的思路是写一个通用的并发控制函数function asyncPool(poolLimit, tasks, handler) { const results []; let running 0; let index 0; return new Promise(resolve { function runNext() { if (index tasks.length running 0) { resolve(results); return; } while (running poolLimit index tasks.length) { const current index; running; handler(tasks[current]) .then(res { results[current] res; }) .finally(() { running--; runNext(); }); } } runNext(); }); }这道题考的不只是异步逻辑还考了类似“滑动窗口”的思想保证正在执行的请求数不超过上限同时尽快把任务队列跑完。如果你能完全独立写出来说明你对Promise的异步流程控制是有真实经验的。5.3 二叉树相关的算法题为何会出现在前端笔试卷很多同学不理解为什么前端笔试还会考二叉树。其实这不奇怪百度对算法基础有一定要求尤其是准备做富交互产品的前端团队会经常遇到递归遍历DOM树、给树形结构做数据转换的场景。我记得第三批的卷子里有一道算法题是“判断一棵二叉树是不是对称的”或者是“二叉树的最大深度”。这类题的核心解法是递归如果你能用非递归栈或队列的方式实现且能分析时间和空间复杂度那绝对加分。对前端来说二叉树题等同于树形结构处理题。比如一个常见的业务场景后端返回一个扁平的列表需要转换成树形结构展示在菜单里。这种题虽然没直接在笔试里出现但面试时概率极高建议提前背好实现方案。6. 这套题复盘从题目看2018年前端面试风向考完笔试不是终点复盘才是真正拉开差距的环节。我在准备面试时把这份卷子吃透了发现它和当年前端社区讨论的热点高度吻合。6.1 手写代码题的比重在增加2018年的前端笔试手写代码题已经占了不少比例不再是一堆选择题走天下。这说明用人单位希望看到候选人不仅能“知道”还能“写出来”。手写题的题型通常集中在数组相关操作去重、扁平化、排序对象相关深拷贝、浅拷贝、防抖节流异步相关Promise、事件循环、并发控制DOM相关实现事件代理、实现一个简单的拖拽这些题目的共同点是面试官已经默认你有能力独立写出这些代码而不是只会调用工具库的API。比如数组扁平化如果你只会arr.flat()而写不出递归实现那笔试就会吃亏。6.2 CSS布局开始向flex和grid转移2018年的时候flex已经非常普及grid也开始在主流浏览器里得到支持。笔试卷里已经出现和flex布局相关的题目比如用flex实现水平垂直居中。不过对于校招的笔试我依然建议你掌握浮动布局的经典写法因为实际项目中老代码可能依然用float而且有些题目会刻意考float的坑比如父元素高度塌陷、清除浮动的几种方式等。6.3 项目经验的考察变得更“隐蔽”线下笔试一般不会直接问你项目经历但会通过开放题来变相考察你的真实经验。比如“如何设计一个前端监控系统”“如果页面白屏了你怎么排查”这类问题不是在考你背概念的能力而是在考你有没有真实处理过线上问题。我在答“页面白屏怎么排查”的时候写了自己的排查思路先按下F12看Console有没有报错。再看Network里资源有没有加载接口有没有返回。再看HTML结构有没有生成判断是JS执行错误还是CSS阻塞。如果是单页应用再查路由配置、动态import是否有路径问题。这个思路不是从任何一本书上看来的是我真的被线上白屏问题折磨过之后总结出来的。这个区分度阅卷人一眼就能看出来。7. 备考建议如果你现在才开始准备应该怎么做很多人问我要2018年的真题其实我更想说的是真题只是载体你通过题复习到的知识点才是真正重要的。我先分享一套通用的复习框架是我后来带人时反复用的。7.1 把知识点归纳成清单逐个过关我建议把前端知识划成几个大块每一块定一个过关标准知识块过关标准HTML能默写常用标签理解语义化CSS能说清盒模型、层叠上下文、BFC能手写常见布局JavaScript能默写闭包、原型链、this指向的例子ES6会用let/const、解构、箭头函数、Promise、async/await浏览器原理能从头到尾讲一遍“输入URL到页面渲染”的过程网络能说清TCP三次握手、HTTP/HTTPS区别、缓存机制框架至少掌握一个主流框架的核心概念和响应式原理光看这个表可能不够我建议每块都找真题来练。只背概念而不动手笔试时看到手写代码题很容易卡壳。7.2 建立自己的“错题本”我自己当时在准备面试时用电子笔记建了一个错题本专门记录那些“看着会一做就错”的题。后来复盘时发现错题本的复习价值远大于任何一本参考书。比如我笔记里有一条是关于事件冒泡和事件委托的。笔试里有时会给一个DOM结构让你判断点击某个元素后事件会经过哪些节点。我一开始老是把冒泡和捕获搞混于是自己画了一张事件传播的示意图捕获阶段从window到目标元素目标阶段在目标元素上冒泡阶段再从目标元素回到window。正是这种动手总结和画图的方式让我的事件机制知识点考得很稳。7.3 真题之外的延伸阅读笔试卷不是孤立存在的。我在做完这份卷子后会顺着题目去阅读相关的资料比如看MDN上关于事件循环的详细介绍看浏览器缓存机制的完整文档研究Promise的规范。以Promise为例如果我只是能看懂这句话“Promise是一种异步编程的解决方案”那我遇到手写Promise.all的题时就会直接懵。但如果我啃过Promise/A规范知道then方法返回的是一个新Promise就很容易理解Promise链式调用的实现原理。笔试的深度题考的就是你对底层原理理解的厚度。8. 几个容易被忽略的笔试细节最后一个部分是经验提醒都是一些容易被忽略、但确实会影响成绩的细节。8.1 看清题目要求和编译器环境笔试平台有的是在线IDE有的只是让你在文本框里写代码。如果是文本框模式多写注释很有必要因为你是没有调试机会的注释能帮阅卷人理解你的思路。我在笔试时习惯在代码开头写一段简短的解题思路比如// 思路用哈希表记录每个元素出现的索引遇到重复时计算距离 // 时间复杂度O(n)空间复杂度O(n)这样既能让阅卷人快速看出你是有逻辑的也不会因为追求代码简洁而让人看不懂。8.2 注意审题特别是“至少”和“尽可能”很多笔试题会有陷阱比如“至少说出三种方案”“手写尽可能完整的实现”如果你只写一种即使完全正确也可能拿不到高分。一个典型的例子是手写深拷贝。如果你只写了最简单的JSON.parse(JSON.stringify(obj))虽然也算一种方案但一旦题目要求“请实现一个较为完整的深拷贝”你至少得考虑循环引用的处理。正则、Date、Map、Set等特殊对象的处理。Symbol和函数属性是否要拷贝。所以笔试题不要急着写先把题目读一遍把要求的关键词圈出来。如果题目说“尽可能完整”就尽量展示你的知识边界哪怕代码长一点也没关系。8.3 卷面整洁合理分配时间在线笔试不涉及手写字迹但代码的排版、命名、注释是有“观感”的。一个变量命名清晰、换行规范的解答和一个把所有代码压在一行的解答即使逻辑一模一样分数也可能不同。时间分配上我的经验是先做会的题把不会的题留到最后。但前提是你要预估每道题的分值不要在一道选择题上浪费10分钟。遇到开放题写一半也比空白要好尽量把自己知道的相关知识点都写上踩不到分也能让阅卷人看到你的思路。我在考第三批这套卷子时最后一道算法题写了一半另一题直接没写完但还是进入了面试。后来复盘时发现可能是我在前面简答题里写了比较完整的思路弥补了后面没写完的缺憾。这里也提醒大家不要因为一题没答好就心态崩溃笔试里大部分人的分数都不是满分的关键是尽量把会做的题都答完整。说到最后我个人的体会是笔试卷是死的但准备过程中的每一次查漏补缺都是活的。2018年的卷子放到今天来看虽然有些题目考察的知识框架和当前前端生态有一些差异但核心的JS功底、浏览器原理、网络基础这几块几乎没有过时。如果你现在正在准备前端校招遇到这类老题不用觉得过时把它们当成基本功的照妖镜就好。做错的题、卡壳的点才是这套题送给你的真正礼物。