ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

前端校招笔试真题解析:欢聚时代2018年A卷考点拆解

前端校招笔试真题解析:欢聚时代2018年A卷考点拆解 1. 试卷全景与考察定位1.1 为什么一份2018年的笔试题现在还值得刷先交代一下背景。欢聚时代就是大家熟知的YY2018年这波校招笔试在当年的前端圈里流传度还挺高原因是它的考察覆盖面比较全且题型设计踩中了不少前端er的痛处。网络上流传的版本有不少残缺很多是候选人考完凭记忆拼出来的原卷已经找不到了。我结合自己做前端面试官的经验和带应届生的复盘笔记以当年的A卷考纲为骨架把核心考点逐一拆开补上解题思路和避坑点。先说结论这份试卷虽然出题年份停在2018但它考察的知识底座到现在都没有过时。HTML、CSS、JavaScript三件套的底层功力浏览器的运行原理经典的算法手写题依旧是大厂校招前端笔试题的固定组成部分。唯一变化的是近几年的试卷里越来越多地加入了TypeScript、工程化、框架运行时原理、微前端等内容但基础模块的权重并没有降低。我之所以推荐大家拿这份题来练是因为它比较适合用来做“知识体检”。一份卷子做完哪些地方卡壳哪些地方能秒答基本能反映出你前端基础的真实水位。而且笔试和面试不太一样笔试考的是你在没有提示、没有交流的情况下能不能独立还原出一个正确的结论。这个能力恰恰是很多培训班出身、习惯看视频边看边敲的同学最容易缺的。对于正在准备校招的应届同学我的建议是不要太纠结“这是哪一年的题”而要把注意力放在“这道题到底在测什么能力”上。下面我按当年A卷的模块结构把每一类题目的考察逻辑和准备方向捋一遍。1.2 试卷结构复盘与各模块权重虽然原始卷面已经不在我手上但根据当时参加笔试的同学反馈整个A卷基本覆盖了以下六类内容。模块常见题型大致权重难度JavaScript基础输出题、原型/闭包/作用域30%中浏览器与网络事件循环、渲染机制、缓存20%中HTML/CSS布局手写布局、盒模型、居中15%中低算法与编程题手写数组/字符串处理、排序20%中高代码阅读与调试给出代码判断输出或bug10%中综合与扩展前端工程化、性能优化5%中高这个比例在今天依然有参考价值。JavaScript永远是前端笔试的核心这类题不太需要背主要靠对语言机制的理解。如果在JS上栽了跟头后面再扎实的框架经验也难救回来因为笔试阶段通常不考框架考的就是你没框架时还能不能写出可靠的代码。1.3 笔试答题的核心姿势线上笔试和面试不一样面试官不在你旁边你思考的过程没人看最终提交的答案就是一切。所以答题顺序很关键我的习惯是先扫一遍全卷把会做的、能拿分的题先标记出来优先死磕会做的再回头啃难题。不要在一道题上死磕超过15分钟尤其是后面的手写算法题往往分值更高、性价比更大。另外手写代码题不要只写代码可以在注释里写清思路。现在不少笔试系统支持运行和判题但即便不支持注释能帮阅卷人理解你的逻辑也方便后续面试环节中面试官拿着你的答卷问细节。我在后来做面试官时最反感的就是答案只给一个函数签名主体逻辑全空着那基本等于没做。2. JavaScript基础题拆解2.1 作用域、闭包与变量提升这份卷子里JS部分的开场题通常是读代码写输出。翻来覆去考的就是几个点var/let/const的区别、变量提升、函数提升、闭包、this指向、原型链。我用当年流传很广的一道经典变形题来演示。var a []; for (var i 0; i 3; i) { a[i] function () { console.log(i); }; } a[0](); a[1](); a[2]();很多人第一次做这道题会以为结果是0、1、2。但真正跑出来是3、3、3。原因在于var i声明的变量是函数作用域整个循环共享同一个i函数被调用时循环早已结束i已经变成3。如果把var i改成let i结果就变成0、1、2。因为let是块级作用域for循环的每次迭代都会创建一个新的绑定。这种题表面考的是作用域实际上背后是“执行上下文”和“闭包捕获变量”的机制。我在实际阅卷时发现很多同学知道该把var改成let但要他说清楚为什么就支支吾吾了。这里有一个比较直观的理解方式你可以把闭包想象成一个小房间房间里的窗户可以看到外面的变量。用var时所有房间共用同一扇窗户窗外只有一台数字显示器用let时每次循环都新建一台显示器每个房间看到的是自己对应的那一台。准备这类题时建议动笔把执行上下文的内存分布画一遍。面试官后续大概率会追问“闭包会造成内存泄漏吗”“怎么避免”如果你能顺手答出“引用计数、标记清除、及时释放不需要的引用”印象分会明显不一样。2.2 this指向与箭头函数this指向几乎是必考点。常见出题形式还是给一段代码让候选人判断输出。核心规律就那么几条按优先级排序new关键字调用this指向新创建的实例call、apply、bind可以显式指定this方法调用谁调用指向谁普通函数调用非严格模式下指向全局对象严格模式下是undefined箭头函数没有自己的this沿用定义时外层作用域的this真题里最喜欢考的是第3条和第4条结合var name window; var obj { name: obj, getName: function () { return this.name; }, }; var fn obj.getName; console.log(fn()); console.log(obj.getName());这里obj.getName()输出obj很好理解。fn()是存了引用之后直接调用相当于普通函数调用在浏览器环境下this指向window输出window。很多人栽在第4条因为没意识到“取方法再调用”会丢失this。顺带说一个容易混淆的点回调函数里写this和箭头函数里写this是两回事。事件监听器、setTimeout回调里的普通函数this指向由调用方式决定而箭头函数则始终指向定义所在作用域的this。理解了这点再遇到“Vue methods里为什么箭头函数不推荐”这类扩展题就能顺藤摸瓜答出来。2.3 原型链与继承原型链是JS模块里区分度很高的一块。2018年的A卷给了一段构造函数和实例的代码要求判断obj.__proto__ Constructor.prototype这类表达式的真假。基本功需要掌握到以下程度function Animal(name) { this.name name; } Animal.prototype.say function () { console.log(this.name); }; var dog new Animal(旺财);此时dog.__proto__ Animal.prototype、Animal.prototype.constructor Animal、dog instanceof Animal这几个表达式的结果都要能一口说出来。再深一层Animal.__proto__ Function.prototype因为Animal本身就是Function构造出来的实例Animal.prototype.__proto__ Object.prototype因为原型链最终会收敛到Object。笔试中关于继承的题一般会让你手写ES5的寄生组合式继承或者让你指出几种常见继承方式的优缺点。我见过大量答案是把“原型链继承”“构造函数继承”“组合继承”混在一起说不清楚。要区分的话记住一个判断标准子类的实例是否共享父类原型上的引用类型属性、父类构造函数是否被调用了一次还是两次。组合继承有个小问题就是父类构造函数被调用了两次而寄生组合式继承就是为了解决这个问题的。3. 浏览器与网络基础题解析3.1 事件循环从经典输出题到微任务宏任务浏览器部分的题目第一梯队就是事件循环。当年那套卷里有一道流传甚广的题大概是这样的变形console.log(1); setTimeout(function () { console.log(2); }, 0); Promise.resolve().then(function () { console.log(3); }); console.log(4);执行结果是1、4、3、2。很多人不理解为什么Promise的回调比setTimeout还早。原因是Promise的then回调属于微任务microtasksetTimeout回调属于宏任务macrotask。当前宏任务执行完后JS引擎会先把微任务队列清空再取下一个宏任务执行。以Chrome的渲染主线程为例可以简化理解为每一次从宏任务队列取一个任务执行过程中如果产生了微任务全部塞进微任务队列当前宏任务结束后按顺序清空微任务队列然后执行渲染相关任务最后再取下一个宏任务。我第一次带新人时经常用“食堂打饭”来比喻宏任务窗口是普通窗口微任务是窗口旁边的小卖部你刚打好饭执行完一个宏任务第一件事是先跑小卖部把酸奶买了清空微任务然后再去下一个普通窗口打饭。关于Promise本身A卷里还有一类改错题比如要求写出Promise.all的替代实现。给出三个异步请求要求全部成功后在页面上展示合并后的数据。核心就是Promise.all的用法以及失败时用catch兜底。这道题看着不难但实际手写时很多同学会忘记Promise.all的入参必须是可迭代对象也容易忘记处理“传入空数组”时的边界情况。准备这类题时建议把Promise的静态方法全过一遍resolve、reject、all、race、allSettled、any每个都要知道入参和返回值。3.2 从输入URL到页面展示必背主链路这部分很少以问答题形式出现在笔试卷里更多是在面试环节追问但笔试也会通过选择或简答题间接考察。最典型的简答题是“在浏览器地址栏输入一个网址到页面渲染完成中间发生了什么。”完整链路可以拆成以下几段DNS解析把域名解析成IP地址中间会经过浏览器缓存、系统缓存、路由器缓存、根域名服务器等层级建立TCP连接三报文握手如果用的是HTTPS建立TLS握手协商加密套件和密钥发送HTTP请求服务器返回响应浏览器拿到HTML后解析HTML生成DOM树解析CSS生成CSSOM树DOM和CSSOM合并成渲染树布局计算各元素的位置和尺寸绘制到页面合成图层这段内容虽然长但它其实是浏览器原理里最好背的。准备方向可以结合性能优化来理解为什么CSS要放头部、JS要放尾部再配合defer和async为什么样式的层级不宜过深这些问题的答案都能在上面这条链路里找到依据。3.3 HTTP缓存与前端网络优化虽然2018年的卷子对HTTP缓存考得没有现在这么重但基础概念还是要过一遍。强制缓存和协商缓存这对概念几乎每家公司的笔试都会碰。强缓存相关响应头是Cache-Control常用值有max-age、no-cache、no-store优先级高于老牌的Expires。协商缓存相关响应头是Last-Modified/If-Modified-Since和ETag/If-None-Match。笔试常考的问答是强缓存命中时返回什么状态码答案是200并且会显示from disk cache或from memory cache。协商缓存命中时返回304。我见过很多人把这两个概念搅在一起其实只要抓住一点强缓存不发请求直接本地取协商缓存是发一个带条件的请求由服务器决定使用缓存还是返回新资源。理解了这套机制再看前端工程化里的文件名指纹逻辑就通了。webpack打包时会在文件名里带上hash本质上就是为了让静态资源更新后能自然跳过强缓存命中重新走一遍协商缓存或直接拉取新文件。这是笔试里常见的综合题很多同学看过webpack配置但从来没想过它为什么那样设计。4. HTML/CSS布局与样式问题4.1 等高布局CSS部分在笔试里一般不会考太偏的题重点集中在盒模型、浮动、定位、Flex、Grid以及几类经典布局的手写题上。其中手写两栏布局和三栏布局的概率出奇的高。三栏布局中比较推荐的做法是flex代码量少边界情况也少。.container { display: flex; } .left { width: 200px; } .middle { flex: 1; } .right { width: 200px; }如果要求中间栏自适应左右固定这个方案就够了。如果想要中间栏优先加载传统做法是圣杯布局或双飞翼布局它们都是基于浮动和负margin实现的。很多同学会把这两个布局的结构原理弄混这里我提供一个记忆方法圣杯布局在container上加padding用相对定位移动左右两栏双飞翼布局在中间栏内部再加一层用margin留出左右空间。前者是“container让位”后者是“中间栏内部让位”。除了布局居中问题也是高频中的高频。水平垂直居中我的首选方案是flex或grid极端兼容场景下再考虑transform或绝对定位。这里有一道容易翻车的问题“不定宽高的元素怎么实现水平垂直居中”只要答出以下这三种方式之一并说明原理即可flex父容器display:flex; align-items:center; justify-content:centergrid父容器display:grid; place-items:center绝对定位左右设为0margin设为auto配合top/bottom设为0值得注意的是绝对定位加margin auto这种方式需要元素有明确的宽高否则内容会塌缩。不设宽高的时候用transform方式更稳妥。4.2 盒模型与BFC笔试题里盒模型基本是送分题但送分也总有人拿不到。关键在于Doctype。在标准模式下box-sizing: content-box时width只包含内容宽度padding和border按标准叠加出去设为border-box时width包含内容padding和控制border。怪异模式是旧IE时代的产物现在基本不用管但面试官偶尔会拿它来测试候选人是否真的理解盒模型的历史背景。BFC看起来有点理论化但结合实际问题就很好记。BFC全称是Block Formatting Context块级格式化上下文。在一个BFC内部子元素的margin不会和外部元素合并float的元素也被包含在BFC的范围内。实际操作上触发BFC的方式包括overflow不为visiblefloat不为nonedisplay为inline-block、table-cell、flex等position为absolute或fixed最常见的应用场景就是清除浮动。父容器内的子元素都float了父容器的高度塌了给父容器加overflow: hidden它就触发BFC子元素的浮动被包含进来高度恢复正常。明白了原理再背十个清除浮动的写法都不如看一眼本质。4.3 手写轮播图时的样式细节2018年A卷的编程题里有一道定时轮播的小题要求实现图片自动切换并且配了左右按钮手动切换。很多人把JavaScript逻辑写了半天却忽略了样式细节结果一排图片水平排列时就出问题。我当时给同事的建议是先用统一容器包住所有轮播项容器宽度设成图片数量的百分比倍每张图片宽度设为容器的几分之一再利用父容器的overflow: hidden裁剪可视区域。切换时用transform的translateX移动容器而不是改left或margin这样可以避开reflow性能更好。.carousel { overflow: hidden; } .carousel-track { display: flex; transition: transform 0.3s ease; } .carousel-slide { flex: 0 0 100%; height: 300px; background-size: cover; }对应js逻辑大概是这样let index 0; const track document.querySelector(.carousel-track); const total 3; function show(i) { if (i 0) { i total - 1; } else if (i total) { i 0; } index i; track.style.transform translateX(-${index * 100}%); } setInterval(() { show(index 1); }, 3000);这套代码虽然简单但体现了几个关键点用transform做动画、用flex避免float布局的麻烦、边界处理时对负索引和越界索引做取模逻辑。这些都是前端开发中真正常用的小能力。5. 算法与手写编程题5.1 常考手写题清单笔试里的编程题一般不会上太难的算法重点落在数组、字符串、排序、去重、防抖节流一类的题目上。我见过的高频手写题按出现概率排序大致是这十个数组去重数组扁平化/拍平防抖与节流深拷贝手写Promise.all字符串反转或回文判断排序算法基础冒泡、快排查找数组最大/最小元素手写bind/call/apply父子树的遍历或查找这里我把数组去重展开讲一下因为它能考察的知识点非常多从Set到Map再到reduce几种方式的复杂度都不一样。最简洁的写法是[...new Set(arr)]但如果你只会这一种面试官追一句“如果要去重对象数组呢”就容易卡住。更好的方式是掌握两层遍历加splice的经典写法再在此基础上给出利用对象键名去重或Map去重的方案。数组扁平化也一样递归版本要会写还要知道Array.prototype.flat的用法和它的默认展开层数。function flatten(arr) { return arr.reduce((prev, cur) { return prev.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []); }5.2 事件循环题目里的输出顺序除了常规算法A卷还有一种编程题是给一张页面要求用setTimeout实现某个间隔执行的效果。这类题不完全考算法而是考异步编程能力。比如要求每一秒打印递增的数字能实现的方式很多。最常见的一种错误解法是for (var i 0; i 5; i) { setTimeout(function () { console.log(i); }, 1000); }这又回到了闭包题的坑。一秒后打印出来的不是0到4而是五个5。正确写法是用letfor (let i 0; i 5; i) { setTimeout(() { console.log(i); }, 1000 * i); }或者用IIFE包一层再或者用bind传参。我在笔试题的阅卷记录里这道题的自定义错误率极高。如果你做出来了但又想真正弄懂建议再想想如果要求每间隔1秒打印一个数字而不是1秒后全部一起打印那么延迟时间应该怎么写。这才是setTimeout题型的核心。5.3 手写防抖与节流的边界条件防抖节流看起来简单但真正手写的时候很多人的代码缺少边界处理。以防抖为例核心逻辑是事件触发后设置一个定时器在等待时间内再次触发就清掉旧定时器并重新计时只有最后一次触发生效。function debounce(fn, delay) { let timer null; return function (...args) { if (timer) { clearTimeout(timer); } timer setTimeout(() { fn.apply(this, args); }, delay); }; }关键问题是this和args。很多初学者写出来的版本在setTimeout里没有保留this指向结果控制台报错或者this指向了window。正确的做法是先用fn.apply(this, args)把当前上下文传进去。节流则相反它的核心在于“限频”一段时间内最多执行一次常见实现有时间戳版本和定时器版本。如果笔试时间允许我建议写完防抖之后顺便把“是否可以立即执行一次”的版本也写好。比如搜索框场景一般希望防抖延迟请求但按钮点击场景可能更希望第一次点击立即触发后续点击被节流。两个版本都练一遍面试时被追问也不慌。6. 框架与工程化启蒙题6.1 从笔试看大厂对框架的要求2018年的校招笔试题几乎不直接考察Vue或React的API调用因为笔试没法验证你真实使用框架时的能力。但会在综合题里隐含地问你数据驱动的本质是什么、组件通信的本质是什么、为什么虚拟DOM能提升性能。我见过A卷流传版本里有这样一道半开放题“Vue中data为什么必须是一个函数”。这题放到今天依然很经典。原因是组件可能被复用如果data直接写成一个对象那多个实例会共享同一个数据引用一个实例改了数据所有实例都会变。写成函数每次创建组件实例时都能返回一份新的数据对象。如果这套题里不出现类似问题面试环节也大概率会问到。所以准备笔试时不要只抱着算法刷框架的底层机制起码要能在“一句话讲清楚”的粒度上回答出来。比如Vue2的响应式原理是Object.definePropertyVue3换成了ProxyReact的setState是异步的状态更新后组件会重新渲染渲染后可以拿到新的props和state。6.2 模块化与打包基本盘工程化相关题在这个阶段不会出得太偏但会考CommonJS和ES Module的区别。这是很多人容易模糊的点。笔试里常见的坑是require是同步加载import是静态导入可以被静态分析所以在编译期就能做依赖分析支持tree-shaking。而CommonJS是运行时加载只能在运行时确定依赖关系。类似地webpack的构建流程要能说出大致链路入口文件解析模块依赖收集loader转换插件处理代码分割最终输出bundle。如果时间紧张理解到“配置里的entry对应入口、output对应输出、loader负责文件转换、plugin负责整体扩展”这个粒度就够了但loader和plugin的区别要能说清。loader是处理文件的转换器plugin是作用于整个构建过程的功能扩展。6.3 移动端适配方案A卷末尾偶尔会出一道移动端点屏幕宽度的题目。核心适配方案有三类meta viewport配合remflexible方案以及viewport单位方案。rem方案的核心是动态设置根节点的font-size然后所有尺寸都用rem来写。假设设计稿是750px宽那么把根字体大小设置为设备宽度除以7.5在375px宽的设备上就是50px。这样设计稿里的100px换算成rem就是2rem。简单理解就是屏幕变宽时所有元素等比例放大。viewport方案更直接直接用vw和vh做单位。1vw等于屏幕宽度的百分之一。设计稿750px那么1px约等于0.1333vw。缺点是需要处理字号太大的场景通常配合min/max做边界限制。这道题不仅考CSS还考你对“响应式”和“自适应”这两个概念的区分笔试里答偏的人不少。7. 实践复盘把真题变成自己的知识地图7.1 考后怎么复盘才不白做做完一套题复盘比刷题更重要。我给自己定过一个标准流程先把错题按知识点归类找出重复出错的知识点比如“this指向”“Promise时序”“CSS居中”这些高频点再去刻意练习同一知识点的不同变形题。复盘时最好写“错题原因”而不是“正确答案”。写下是因为概念不熟还是因为看漏了条件还是因为边界没处理。这三类原因对应的复习策略完全不同。概念不熟就回头看文档和书籍看漏条件就训练读题习惯边界没处理就去多写不同输入值的测试用例。另外一个建议是把每道题的代码在真实环境里跑一遍不要只用眼睛看输出。事件循环和this指向这类题目直接在浏览器控制台里实际执行所见即所得比背文章有用得多。跑完之后再试着改改条件比如把普通函数换成箭头函数把setTimeout的延迟时间改成0看看输出会不会变。这样一题变多题一套卷子能吃透的知识量是原来的三倍。7.2 笔试答题时间分配的经验线上线下笔试一般给两个小时左右。以A卷的题量和结构来说我的建议是前30分钟把比较简单的基础题和输出题一次性写完中间40分钟处理重头的手写题剩下30分钟做算法和综合题最后20分钟检查。检查的重点不是看答案对不对而是看有没有漏条件。常见的情况是排序题没有考虑空数组数组去重题没有考虑NaN防抖题没有传this。这些边界条件不会在示例输入里暴露但判题系统有时候就会卡在这些奇怪的地方。7.3 由一道题延伸到面试问答笔试结束后通常会有一个拿着你的笔试卷子追问的环节。这时候如果你只写了一个正确答案而没有想过背后的原理会显得准备不足。比如你写出了数组去重的Set写法面试官大概率会问“Set去重和Map去重有什么差别”“能不能不用Set写”如果只背答案这两个问题会当场卡住。我的习惯是每做一道笔试题都顺手准备两个引申问题。比如完成深拷贝后能回答“如何处理循环引用Set和WeakMap有什么差别为什么用WeakMap”完成防抖后能回答“防抖和节流的本质区别是什么哪些场景用防抖哪些场景用节流”这样笔试不只是笔试它变成了一轮模拟面试。我见过不少同学把题库刷了三遍但成绩始终徘徊在及格线。问题往往不在于题量而在于“只做题不复盘”。把每一道题当成一个知识锚点连成一张网比刷一百道题再忘掉九十九道要有效得多。最后再分享一个小习惯每次模拟笔试之后我会把当时的错误代码单独存一个文件持续积累。过两周再回来重新跑一遍看自己能不能自行修复。用这种方式来检验知识掌握程度比反复翻看正确答案更能暴露问题。这份欢聚时代2018校招A卷我的建议不是做完就扔而是留档作为阶段性自测的标尺每隔一段时间回头重做一次你会看到自己的进步曲线。
返回列表