校招前端笔试高频考点解析:从闭包、事件循环到浏览器缓存
早两年校招季前端岗的笔试几乎是清一色的“JS基础手写题”欢聚时代2018校招那套web前端B卷算是其中很有代表性的。整体给人的感觉是只要你认真写过半年以上页面大部分题都能动笔但能不能拿高分取决于你平时写代码时有没有琢磨过“为什么”。这份试卷放到现在准备校招、社招的同学手上依然有参考价值因为它的核心考点——作用域、闭包、异步、布局、缓存、工程化——恰好是web前端开发这行吃了十年还没过时的基本功。就算你已经工作几年把它当一套基础自测题做一遍也能发现自己哪些地方“会用但讲不清”。这篇文章不打算贴“原题答案”而是从出题人的视角把题拆开讲讲每类题目在考什么、怎么答能得分、有哪些现场容易翻车的点。也欢迎你自己先找张纸限时90分钟做一遍再回来对思路。1. 试卷整体考察思路2018年校招web前端要的是什么样的人1.1 一份B卷背后的岗位画像看一份笔试之前先要搞清楚公司要招什么样的人。2018年前端环境大概是什么状态Vue 2和React 16已经大面积落地webpack 4刚出不久ES6基本成为默认语法小程序开始冒头TypeScript还没有像今天这样成为必选项。所以那个阶段的校招面试官最担心的不是“候选人不会新框架”而是“基础不牢、上来就背框架API”。B卷作为非第一志愿或补录批次的题目风格一般比A卷更“稳”。A卷敢出偏题怪题去筛尖子B卷更看重平均水平——能不能干活、有没有基本工程素养、代码习惯好不好。这导致B卷里大量的题都落在“语言基础、浏览器机制、手写代码”这三个安全区里真正的坑往往藏在“看起来会做但又没完全会做”的题上。我当时带团队面试新人最喜欢看的就是这种卷子的简答题。选择题可以蒙代码题可以抄但简答题要你用几十个字讲清楚一个概念三句话就能看出你是背的还是懂的。1.2 题型分布与答题时间策略按常见B卷的结构题型大致有这么几块题型考察方向参考分值建议用时选择题基础记忆、易混淆概念20分左右10~15分钟简答题概念归纳、原理表达20分左右15~20分钟手写代码题编码能力、边界意识40分左右35~40分钟综合/开放题方案设计、性能优化20分左右10~15分钟这里有个反直觉的建议先把手写代码题看完再回头做选择题。手写题通常放在试卷中后段分值最重如果你一开始在选择题上卡了太久后面代码题可能连读题时间都不够。我见过很多考生前面选择做得特别爽最后一道20分的综合题只写了三行字非常可惜。如果按100分估算代码题基本占了四成。这说明出题人默认“能写出来”比“能说出来”更有说服力。你答选择题的时候心里就要有数后面还有几道大代码题在等你该放弃的不要恋战。2. 核心考点逐个拆解JS、浏览器、CSS这些题到底在问什么2.1 变量提升与闭包输出题到底在问什么B卷第一类高频题是“下列代码输出什么”。这类题看起来是送分其实区分度极高。比如下面这个经典写法for (var i 0; i 5; i) { setTimeout(function () { console.log(i); }, 100); }如果没学过闭包可能脱口而出“0、1、2、3、4”实际输出是五个5。原因是var声明的i挂在全局/函数作用域上循环结束后i已经变成5定时器回调执行时读到的都是同一个i。改成let i或者用IIFE包一层才能输出01234。这道题扩展出去还有一串考法函数声明与变量声明的提升顺序、let的暂时性死区、typeof一个未声明变量的结果。比如typeof a如果a未声明结果是undefined而不是报错但如果先console.log(a)再var a 1结果是undefined而不会抛异常。这些细节不是要你背而是在考你脑子里有没有完整的执行上下文模型。闭包的隐藏考点还不止于此。面试官会追问“闭包会造成什么问题”——典型答案是内存泄漏风险因为内部函数长期持有外部变量引用外部变量无法被回收。但如果你能补一句“现代浏览器引擎已经做了优化不是所有闭包都会泄漏只有被外部引用逃逸的才会”这题基本就满分了。2.2 事件循环Promise、setTimeout、async的先后顺序异步题是前端笔试的“必争之地”。B卷里最经典的一道长这样console.log(script start); setTimeout(function () { console.log(setTimeout); }, 0); Promise.resolve() .then(function () { console.log(promise1); }) .then(function () { console.log(promise2); }); console.log(script end);输出顺序是script start、script end、promise1、promise2、setTimeout。原理是JS引擎把任务分为宏任务和微任务同步代码执行完后先清空当前宏任务下的所有微任务队列再取下一个宏任务。Promise.then属于微任务setTimeout属于宏任务所以即使setTimeout延迟0毫秒也得排在微任务后面。2018年的卷子还不流行async/await的输出题但放到现在你得把await看成“让出线程的Promise语法糖”。await fn()后面的代码相当于.then里的回调整体还是按微任务优先级来排。有些同学把async当成同步函数一遇到await就卡壳本质还是没有理解事件循环的推进顺序。这部分想拿稳分建议自己画一张“执行栈 宏任务队列 微任务队列”的图把定时器、Promise、DOM事件、requestAnimationFrame分别归类。能把微任务比作“插队窗口”、宏任务比作“排队窗口”讲给面试官听说明你真懂了。2.3 CSS布局垂直居中、BFC被问烂但还能翻车CSS题一般不会太难但“垂直居中”这种题几乎年年见。2018年标准的答案已经不再是margin:auto那套负margin方案而是flex大法.parent { display: flex; align-items: center; justify-content: center; }但如果出题人加一句“父容器高度不固定”很多人就开始犹豫。其实flex方案和grid方案都不依赖父容器高度align-items: center本来就是拉伸后的交叉轴居中。真正容易丢分的是“子元素定宽定高”时用absolute transform绝对居中少写一个top: 50%这种小错误在笔试现场一紧张就会犯。另一个必考点是BFC块级格式化上下文。一道常见题是父容器里的子元素设置了float父容器高度塌陷怎么解决标准答案是触发父容器的BFC具体手段包括overflow: hidden、display: flow-root、display: inline-block、position: absolute。但要真讲清楚BFC是什么需要回到“格式化上下文”这个概念BFC内部的布局不会影响外部浮动元素的高度在计算时会参与父容器的高度计算。很多人会把overflow: hidden当成“万能清除浮动”而不知其所以然一旦面试官追问原因就露馅。我的建议是答这一类题时说“让父容器形成独立的BFC内部浮动元素不再溢出”这句话体现的是原理层面的理解比背三种触发方式有用得多。2.4 浏览器缓存与HTTP状态码能不能把ETag讲清楚简答题里十有八九会出现“强缓存与协商缓存的区别”。强缓存是浏览器不向服务器发请求直接本地读对应Expires和Cache-Control协商缓存是浏览器带着标识问服务器“这个资源还能不能用”对应Last-Modified和ETag。这里有一个非常好的答题框架直接套用就能拿高分缓存类型关键响应头判断依据状态码强缓存Cache-Control: max-age当前时间是否在有效期内200 (from disk cache)协商缓存Last-Modified / If-Modified-Since修改时间是否变化304协商缓存ETag / If-None-Match内容标识是否变化304如果只答到这里大概能拿七成分。想拿满分要补充两句话第一Cache-Control的优先级高于Expires现在主流推荐只用Cache-Control第二ETag比Last-Modified更可靠因为修改时间无法解决“文件改了内容但修改时间被还原”或者“同一秒内多次修改”的问题。我那时候笔试答这道题顺手写了“静态资源文件名带hash指纹可以把缓存时间设到一年上线时无感知更新”面试官在卷子上打了个勾。这说明什么说明校招笔试不是只考记忆更考你有没有真实发布经验。3. 手写代码题的解题套路从读题到能跑通的完整思维链3.1 防抖与节流写的不是函数是边界意识手写题里出现频率最高的就是“防抖节流”。防抖debounce是“事件停止触发后等待t毫秒再执行”适合搜索输入节流throttle是“每隔t毫秒最多执行一次”适合滚动和 resize。一个能直接过面试的防抖实现长这样function debounce(fn, wait, immediate) { let timer null; return function () { const context this; const args arguments; const callNow immediate !timer; clearTimeout(timer); timer setTimeout(() { timer null; if (!immediate) fn.apply(context, args); }, wait); if (callNow) fn.apply(context, args); }; }这里有几个细节恰恰是评分点。第一this指向必须绑定到调用防抖函数的对象上不能直接用fn()调用第二要处理参数透传用arguments或者剩余参数第三immediate参数表示首次是否立即触发。很多考生只写了个简单版没有处理this一看就知道是从博客抄的看不懂为什么需要apply。节流的实现思路也差不多可以用时间戳记录上次执行时间也可以用定时器。我在批改时最看重的是“定时器初始化时是否置空”这能看出来候选人会不会内存泄漏的常见坑。顺带提一句只要把这两个函数写熟笔试里的“实现一个函数防抖/节流”基本就能快速拿分是性价比最高的手写题。3.2 深拷贝的边界从JSON.parse到循环引用几乎每套前端笔试题都有深拷贝。最简单的答案JSON.parse(JSON.stringify(obj))能拿两分因为它有致命缺陷无法处理undefined、函数、Symbol、正则、Date更处理不了循环引用。递归版本是大多数人的选择function deepClone(obj, map new WeakMap()) { if (typeof obj ! object || obj null) return obj; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); if (map.has(obj)) return map.get(obj); const clone Array.isArray(obj) ? [] : {}; map.set(obj, clone); for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { clone[key] deepClone(obj[key], map); } } return clone; }这里的核心加分点有两个一是用WeakMap记录已拷贝对象解决循环引用二是对Date、RegExp做特判避免它们被打散成普通对象。笔试现场时间紧张能写出递归版本已经不错但如果你能主动提到“循环引用会导致栈溢出需要用一个Map记录克隆过的对象”就已经超越大部分考生了。我经常跟新人说深拷贝是检验“数据结构意识”最好的题目。很多人背了代码但没想过为什么用WeakMap而不用Map——因为WeakMap的键是弱引用不会阻止对象被垃圾回收在拷贝大对象时更安全。这层原因讲清楚比写完整个函数更让面试官记住你。3.3 数组去重的“三代”写法数组去重看似简单但要写出层级感一样能看出水平。第一代是双重循环能跑但效率低第二代是indexOf或includes简洁很多第三代是Setconst unique [...new Set(arr)];如果到这里就停还是不够。真正拉开差距的是边界情况NaN在Set里只有一个用indexOf去重时NaN永远找不到字符串和数字1与1在Set里是两个不同值如果数组里还有对象Set去重要求引用相同不能按内容去重。加分的做法是补充“需要按属性去重”的场景用Map的键来做唯一性判断。比如根据id字段去重const uniqueById [...new Map(arr.map((item) [item.id, item])).values()];这样一道“数组去重”题你可以从最简单写法一路聊到复杂场景既展示基础熟悉度又展示工程思维。笔试不是只看结论对错更看你能不能把知识点串起来。3.4 综合题思路实现一个模板字符串解析有些B卷会出一道综合题比如“实现一个简易模板引擎”输入这样的字符串var str Hello, {name}! Your score is {score}.; var data { name: Alice, score: 95 };期望输出Hello, Alice! Your score is 95.并且要求考虑数据缺失的情况。这题的解析思路很简单用正则匹配花括号内容再替换成对应属性function render(template, data) { return template.replace(/\{(\w)\}/g, (match, key) { return key in data ? data[key] : ; }); }这道题在网上流传的版本里还有一个变形要求支持{user.name}这种路径访问。这时候就不能用简单的key得把点号路径拆开逐层取属性function resolvePath(obj, path) { return path.split(.).reduce((acc, key) (acc null ? acc : acc[key]), obj); }综合题给我们的启示是出题人不会考你背诵某个API而是考你“面对一个开放式需求怎么拆解”——先拆出占位符再拆出数据查找逻辑最后考虑边界。如果你在答题卡上能写出“先用正则匹配再递归解析嵌套属性”这个思路哪怕代码没写完也能拿到一半分。4. 真正拉开差距的题目工程化与框架初探4.1 模块化三兄弟CommonJS、AMD、ESModule2018年前后的笔试题特别喜欢考模块化规范。原因也简单那时候webpack已经成为标配但很多人只会配置loader不理解模块系统怎么工作。CommonJS是Node用的require是同步加载模块在本地磁盘上读文件很快所以同步没问题。AMD是浏览器早期用的代表是RequireJS为了解决浏览器里文件异步加载的问题所以设计了define包裹的回调方式。ESModule是语言层面的规范import静态导入支持tree shaking是前端开发后来的主流方向。笔试里最常见的问法是“CommonJS和ESModule有什么区别”参考答法CommonJS导出的是值的拷贝ESModule导出的是值的引用CommonJS可以动态requireESModuleimport语句必须写在顶层CommonJS是运行时加载ESModule是编译时输出接口如果模块内部改了值CommonJS进来的一方拿不到最新值ESModule能拿到。如果能再补一句“ESModule在编译阶段就能分析出哪些导出没被使用从而被摇树优化掉”说明你对webpack的tree shaking也有概念这在工程化部分非常加分。4.2 Vue与React的对比题会选更要会讲校招笔试题里框架题通常不会太深一般停留在“用过哪个、怎么做数据交互”这种层面。但如果出题人想挑有真实项目经验的人会出对比型简答题比如“v-if和v-show有什么区别”“computed和watch怎么选”。v-if是条件渲染不满足条件时不渲染DOM节点v-show是CSS切换节点一直存在。频繁切换的场景用v-show初始化时条件基本不会变的用v-if。这是送分题。computed和watch则要有更深的理解。computed是“由响应式依赖计算出来的值并且有缓存”如果依赖没变多次读取不会重新计算watch是“观察某个值的变化执行对应副作用”适合异步操作或相对复杂的业务逻辑。答题时举一个具体例子比较好列表筛选用computed搜索框输入后请求后端接口用watch。那个阶段React也在高频出现比如“setState是同步还是异步”。标准解释是在React合成事件和生命周期钩子里是异步批量的在原生DOM事件和setTimeout里是同步的不是setState本身异步而是React的更新调度机制。能答到这一层说明真的遇到过React的优化机制而不是只会写JSX。4.3 从URL输入到页面渲染一道压轴级的综合题“在浏览器地址栏输入URL并回车到页面完整渲染出来中间发生了什么”几乎是前端面试的超级综合题。它横跨网络、浏览器、渲染三个领域非常适合出在B卷压轴——因为即使你在某个环节了解不深其他环节也能拿分。这道题的标准回答路径是DNS解析域名得到IP建立TCP连接如果是HTTPS还要完成TLS握手发送HTTP请求服务器返回HTML浏览器按HTML生成DOM树同时解析CSS生成CSSOM树两者合成渲染树浏览器根据渲染树计算布局layout最后绘制到屏幕。过程中遇到script标签还可能阻塞DOM解析遇到图片会异步加载。想拿高分的延展点有这些DNS解析有本地缓存、路由器缓存、递归服务器等多级查询现代浏览器一般会建立6个左右同域TCP连接实现并发请求HTML解析过程中遇到没有async或defer属性的普通脚本会暂停DOM解析CSS默认不会阻塞DOM解析但会阻塞渲染因为渲染树依赖CSSOMDOMContentLoaded与load事件的区别在于是否等到图片等资源加载完。2018年这道题的考点还是完整链路放到今天还可以补充HTTP/2多路复用、preload预加载、content-visibility跳过屏外渲染等新机制。但核心链路不变底层逻辑还是那些。把这道题背熟对理解前端性能优化有非常大的帮助。5. 答题策略复盘与踩坑记录5.1 三个最容易丢分的现场细节第一手写题不写注释。不是非要你写注释而是在关键行后面补一句“这里清空定时器是为了避免重复触发”一方面帮自己理清思路另一方面让阅卷人一眼看到重点。很多人写对了但阅卷人找不到得分点非常亏。第二简答题只写一句话。比如“v-if和v-show的区别是一个渲染一个不渲染”这句话没错但太单薄。聪明写法是“v-if是条件不满足时不渲染DOM属于真正的按需渲染v-show是节点始终存在只是切换display属性。频繁切换用v-show初始条件就几乎不变用v-if。”先结论、后原理、再举例这个结构在笔试题里非常吃香。第三遇到不会的题直接留白。其实填空题、简答题写上你推断的答案大概率有分就算不完全对也能让阅卷人看到你的思考路径。代码题不会写完整实现把伪代码写出来把思路一步步列出来也能拿一半分。我批过很多卷子最怕的就是那种大面积空白想给分都找不到落点。5.2 答题时间分配与做题顺序再强调一遍顺序开考后前5分钟快速浏览全卷标记出代码题的位置和分值然后按“选择 - 简答 - 手写 - 综合”的顺序做但每一类题型内先做有把握的。选择题控制在每题1分钟之内犹豫超过2分钟直接标记跳过。简答题每题控制在5分钟名词解释只要点出核心概念加一句应用场景就足够。手写题每道控制15分钟左右先搭框架再补边界不要一上来就在编辑器式头脑里抠细节。如果最后还剩10分钟回头检查两个地方一是代码题有没有写分号、括号匹配二是综合题有没有漏掉“考虑异常输入”这种加分语句。这种临门一脚的细节经常能救回5分左右。5.3 现在回头看这些考点还重要吗这套B卷放在2018年看代表的是那个时代对“基础扎实的web前端开发”的期待。放到今天考点重心肯定有变化TypeScript成为基本盘框架考察深入到源码级diff和响应式原理工程化变成场景题而不是概念题还多出了性能监控、微前端、AI辅助开发等新方向。但底层的东西一点没变。事件循环、作用域闭包、缓存策略、渲染流程依然是你排查任何前端线上问题的根基。当年考的是手写防抖今天考的是“为什么这种频繁触发的场景会卡顿怎么解决”当年考的是深拷贝循环引用今天考的是不可变数据在状态管理里的意义。题型变了问题本质没变。所以如果你现在准备前端相关岗位我建议别把这份老卷子当成“真题背诵素材”而是当成“基础自测清单”。做错的地方不要只看正确答案去翻一翻对应的MDN文档或源码实现把“这个API为什么这么设计”想明白比多刷十套题都管用。我个人在带新人时还会让他们把这套题重新做一遍但要求用TypeScript重写手写题并补上单元测试。这一下就把“会做题”升级成了“会工程化”也顺便检验了代码风格和测试意识。说到底笔试只是入场券能持续把代码写清楚、把问题讲明白的人才是这个岗位真正需要的人。

相关新闻

最新新闻

日新闻

周新闻

月新闻