ARTICLE DETAIL

资讯详情

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

前端校招笔试题详解:从JS基础到事件循环与缓存策略

前端校招笔试题详解:从JS基础到事件循环与缓存策略 最近好几个学弟学妹都在准备秋招有人拿着B站2023校园招聘前端开发方向笔试卷A问我这题值不值得刷。我的看法很直接这套卷子的含金量不在于题目本身而在于它把前端校招最常考、最容易翻车的一批知识点全串了一遍。不管你是应届生、打算年后跳槽的初级前端还是带新人的老手按这套卷子的考点做一次自查基本能摸清自己基础技能的底子。这篇文章我会尽量还原卷面的题型结构逐个拆解高频考点背后的原理把代码输出题的推理过程、手写题的解题思路都摊开讲。每道题我都会给出分析和参考写法还会补充一些笔试时容易踩的坑。内容偏长建议收藏后跟着敲一遍尤其是手写题光看不练真的会翻车。1. 整套卷子到底在考什么1.1 题型分布与答题节奏参考这套卷子整体风格偏基础加综合题型大体分四块选择题、代码输出题、手写编程题、简单问答题。按照常见校招笔试的配置90分钟左右的题量里选择题大概20道上下输出题5到8道手写题2到3道问答题1到2道。分值占比通常和题量挂钩但更关键的是考察方向题型大致题量主要考察方向选择题单选/多选20道左右JS基础、CSS布局、网络协议、浏览器缓存代码输出题5-8道作用域、this指向、事件循环、闭包手写编程题2-3道防抖节流、深拷贝、数组处理、算法基础问答题1-2道Vue/React原理、性能优化方案、工程化理解答题节奏上我建议选择题平均一分钟内解决遇到拿不准的不要恋战先标记跳过。输出题是拿分大项每题控制在三到五分钟推理过程写在草稿纸或者代码注释里方便回头检查。手写编程题一题至少留十五到二十分钟这类题分值高、批改标准也最硬跑不出结果基本就是零分。问答题放在最后只要写了要点就有分别空着。1.2 考点背后的能力模型B站这类平台公司招前端校招生其实不是想找一个“API调用熟练工”而是希望候选人具备四个底层能力语言基础扎实、理解浏览器运行环境、具备工程化意识、算法思维够用。语言基础对应JS的变量提升、作用域、原型链、异步这些老生常谈浏览器环境对应渲染机制、缓存、安全策略工程化对应模块化、构建工具、代码规范、组件封装算法思维不要求你手撕红黑树但常见的排序、去重、递归、大数处理要能写出来。这套卷子的题目基本都是围绕这四块出的了解这层逻辑之后再刷题心里会更有底不会每道题都当成孤立的碎片去背。2. JavaScript基础考点最容易拉开差距的细节2.1 变量提升与暂时性死区JS基础部分变量提升是选择题和输出题的高频常客。看这段代码console.log(a); // 输出什么 var a 1;很多人第一时间反应是“报错”但正确输出是undefined。原因是var声明的变量会被提升到当前作用域顶部但赋值操作还停留在原地。上面的代码等价于var a; console.log(a); a 1;let和const就不一样了它们虽然也有提升但在声明之前访问会进入暂时性死区直接抛ReferenceError。我笔试时见过一道很经典的组合题var b 10; function fn() { console.log(b); let b 20; } fn();这题输出不是10而是直接报错。很多人的误区在于“外层有var b所以内部能读到”实际上let b在自己的块级作用域里已经把b“锁”住了在声明前访问就触发暂时性死区。理解这个机制的关键点在于JS 在执行一段代码前会先完成“变量登记”var登记时初始化为undefinedlet/const登记时不初始化直到执行到声明语句才绑定值。2.2 闭包与this指向的组合拳输出题里闭包和this经常是一起出现的。先看一个几乎是标配的循环闭包题for (var i 0; i 3; i) { setTimeout(function () { console.log(i); }, 100); } // 输出什么答案是3 3 3不是0 1 2。原因是var声明的i属于全局作用域三轮循环结束后i已经变成3三个定时器拿到的都是同一个变量。想要输出0 1 2有三种改法把var改成let或者用 IIFE 传参或者用setTimeout的第三个参数for (var i 0; i 3; i) { setTimeout(function (n) { console.log(n); }, 100, i); }这套题考的其实是“闭包保留的是变量引用不是值”这个底层逻辑。至于this指向记住一条核心规则this指向函数被调用时的执行上下文而不是定义时所在的对象。箭头函数除外它的this是在定义时从外层作用域捕获的。笔试里常出的变体包括对象方法拆出来调用、call/apply/bind改变指向、函数内部嵌套普通函数导致this丢失这几类最好都亲手写一遍。2.3 原型链与继承的底层逻辑原型链这块选择题喜欢出instanceof的判断问答题喜欢让手写继承。先看instanceof的原理它检查的是构造函数的prototype是否出现在对象的原型链上。function Person(name) { this.name name; } const p new Person(zhang); console.log(p instanceof Person); // true console.log(p instanceof Object); // true手写继承是手写题的高发区ES6 的class虽然简单但笔试有时会要求用 ES5 实现。一个比较标准的寄生组合式继承可以这么写function Parent(name) { this.name name; } Parent.prototype.sayName function () { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); this.age age; } Child.prototype Object.create(Parent.prototype); Child.prototype.constructor Child;这里重点说下为什么不能直接Child.prototype Parent.prototype。这样赋值会导致父子原型对象完全共享往Child.prototype上添加方法时Parent.prototype也会被改掉污染父类。Object.create(Parent.prototype)则创建了一个以父类原型为原型的中间对象既继承了父类方法又不会反向污染。2.4 事件循环与异步输出顺序异步输出题基本是这套卷子的压轴题型因为它的推理链条够长能一次性考察宏任务、微任务、Promise、async/await多个知识点。先给一个经典例子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。关键点在于Promise.then属于微任务在当前宏任务的同步代码执行完之后立即执行setTimeout属于宏任务要等到下一个宏任务阶段才执行。很多人在笔试时卡在setTimeout和Promise到底谁先谁后记住一句话宏任务先排队微任务清空后再取下一个宏任务。async/await的版本再复杂一点async function test() { console.log(a); await Promise.resolve(); console.log(b); } test(); console.log(c);输出是a、c、b。注意await后面的代码相当于被放进了.then回调里属于微任务。在浏览器环境里console.log(c)先于微任务执行所以b最后才打印。这类题自己敲一遍比死记十个例子都管用因为每次执行顺序的差异都能帮你在脑中建立“任务队列”的模型。3. 浏览器、网络与性能优化3.1 HTTP缓存机制强缓存与协商缓存浏览器网络部分缓存是必考项。核心要分清两种缓存强缓存和协商缓存。强缓存由Cache-Control和Expires控制命中后直接从本地读根本不会发请求到服务器协商缓存由ETag/If-None-Match和Last-Modified/If-Modified-Since控制需要和服务器确认资源是否变化。缓存类型主要字段判断过程触发条件强缓存Cache-Control, Expires直接读本地不发请求缓存未过期协商缓存ETag/If-None-Match, Last-Modified/If-Modified-Since发请求给服务器判断强缓存未命中实际开发中面试官常追问“强缓存过期后怎么办”答案是进入协商缓存流程浏览器带着If-None-Match或If-Modified-Since请求服务器服务器返回304 Not Modified浏览器继续用本地缓存只有资源真的变了才返回200和新资源。写业务的时候我习惯给index.html设置Cache-Control: no-cache给带 hash 的静态资源设置max-age31536000这样既能保证入口文件及时更新又能让静态资源最大化利用本地缓存。3.2 浏览器渲染流程与回流重绘渲染机制这块笔试常考“从输入URL到页面展示发生了什么”或者在问答题里让你说“如何减少回流和重绘”。整个渲染流程大致是解析 HTML 生成 DOM 树解析 CSS 生成 CSSOM两者合并成渲染树然后进行布局Layout计算几何信息最后绘制Paint到屏幕上。回流说的是布局阶段重新计算几何属性重绘说的是像素更新。最直观的理解改变宽度、高度、字体大小等几何属性会触发回流只改颜色、背景图等视觉属性只触发重绘。回流的开销远大于重绘。写代码时减少回流的手段包括用transform代替top/left做动画把频繁读取布局属性的操作合并用DocumentFragment批量操作 DOM这些都属于加分项。3.3 安全基础XSS与CSRF浏览器安全防线上XSS 和 CSRF 是问答题老面孔。XSS 的完整叫法是跨站脚本攻击攻击方式是把恶意脚本注入到页面里执行。最常见的防御手段是转义用户输入的内容在 React 和 Vue 里框架本身就做了默认转义但如果你用v-html或者dangerouslySetInnerHTML直接渲染用户输入就要格外小心。CSRF 是跨站请求伪造攻击原理是借助用户在目标网站已登录的 Cookie诱导用户访问恶意页面并发送伪造请求。防御方案主要是校验Referer、使用 CSRF Token以及把关键接口的 Cookie 设置为SameSite。这一节内容不难但能体现一个候选人是否写过真实业务、有没有安全意识。4. 框架与工程化Vue核心与前端基建4.1 Vue响应式原理与diff框架题在这套卷子里通常以选择题或问答题出现Vue 的概率很大。Vue 2 的响应式原理是基于Object.defineProperty遍历 data 对象的每个属性做数据劫持Vue 3 改成了Proxy代理整个对象。后者最大的优势是不需要提前递归遍历属性新增删除属性也能被响应式系统捕获性能上更优。关于v-for里的key卷子喜欢问“为什么不能用 index 作为 key”。原因是 diff 算法会通过 key 判断新旧虚拟节点的对应关系用 index 做 key 时如果列表头部插入了一条数据后面的节点全部要重新渲染造成不必要的性能损耗。输出题不太会考这块但问答题很爱从框架原理切到实际项目。nextTick也是高频考点它的作用是等 DOM 更新完成后再执行回调。比如在 Vue 里修改了响应式数据立刻操作 DOM 可能拿不到最新的值因为 DOM 更新是异步的。nextTick的实现原理是把回调放进一个微任务队列在下次 DOM 更新循环结束后执行理解了事件循环再看nextTick就水到渠成了。4.2 前端工程化与代码规范实践工程化部分卷子会考察模块化、构建工具、代码规范这些“软实力”。模块化方面ES Module 和 CommonJS 的区别是送分题export/import是静态分析、在编译时确定依赖关系module.exports/require是动态加载、在运行时确定。ES Module 可以做 tree-shakingCommonJS 不行这就是为什么现在新项目都优先用 ESM。“前端开发规范vue”这个话题这几年越来越热笔试可能不会直接考规范的条目但问答题会让你“说说项目里如何保证代码质量”。比较落地的答案是用 ESLint 做静态检查、Prettier 统一格式化、husky 在 git commit 前跑 lint 和单测再加一套 component 目录划分规范。我在项目里一般把组件目录按components/业务模块/组件名/index.vue组织每个组件只负责一个职责超过三百行就要考虑拆分。4.3 前后端协作和组件封装细节现在不少校招卷会加一道“前后端联调时遇到的问题与解决”的开放题这时候懂一点接口设计和参数处理就很有优势。比如后端返回字段不统一、嵌套层级深前端写一堆user.info.address.city很容易出错。比较专业的做法是前端做一层数据适配器把后端字段转换成页面需要的结构后续后端改动只改适配器一处。组件封装方面...arg这个写法经常出现它叫剩余参数或展开运算符在二次封装组件时特别常用。比如要封装一个带默认样式的按钮组件但允许使用方透传原生按钮的所有属性template button classbase-btn v-bind$attrs slot / /button /template在 React 里写法就是const MyButton ({ ...props }) button classNamebase-btn {...props} /。这个细节能看出候选人写组件时有没有“可复用、可扩展”的意识。顺带提一句这两年 AI 辅助编码确实能提速不少团队也在尝试 AI 生成基础页面笔试场上通常不给用工具但平时练习可以拿它当“结对搭档”让它帮你 review 代码会开阔不少思路。5. 编程题实战解析5.1 手写防抖和节流防抖和节流是手写题里的“保底题”几乎每家都会考。一个容易混淆的点防抖是“停止触发后延时执行”节流是“固定间隔内最多执行一次”。业务场景里搜索框输入用防抖滚动监听、窗口缩放用节流。标准防抖写法function debounce(fn, wait) { let timer null; return function (...args) { if (timer) clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); }, wait); }; }标准节流写法function throttle(fn, interval) { let lastTime 0; return function (...args) { const now Date.now(); if (now - lastTime interval) { lastTime now; fn.apply(this, args); } }; }两个函数里我都刻意用了...args和fn.apply(this, args)这能保证回调里的this指向不出错笔试时写上这行是加分项。别只写fn()那样即便逻辑对了严谨性上也差了一截。5.2 手写深拷贝深拷贝是另一个高概率题。很多人会直接写JSON.parse(JSON.stringify(obj))这招面试官肯定要追问遇到undefined、函数、Symbol、循环引用怎么办所以笔试如果要求手写深拷贝最好直接上一个能处理常见边界条件的版本function deepClone(obj, map new WeakMap()) { if (obj null || typeof obj ! object) return obj; if (map.has(obj)) return map.get(obj); if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); const clone Array.isArray(obj) ? [] : {}; map.set(obj, clone); Reflect.ownKeys(obj).forEach((key) { clone[key] deepClone(obj[key], map); }); return clone; }WeakMap专门用来处理循环引用Reflect.ownKeys可以拿到包括 Symbol 在内的所有属性。整个写法既不过度复杂又能覆盖大部分笔试题的边界要求。手写题不是越炫越好关键是结构清晰、边界完整阅卷人能一眼看懂你的思路。5.3 数组去重与扁平化数组去重属于基础中的基础但考察方式可以变出花来。简单版本用Set就够了const unique (arr) [...new Set(arr)];如果要求去重对象数组根据某个字段去重就得用Mapconst uniqueBy (arr, key) { const map new Map(); arr.forEach((item) { if (!map.has(item[key])) map.set(item[key], item); }); return [...map.values()]; };数组扁平化也常考。进阶要求是“手写 reduce 实现 flat”function flatten(arr) { return arr.reduce((acc, cur) { return acc.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []); }如果数组层数很深还可以加一个depth参数控制扁平化层数。这类题目没什么高深技巧但很考验代码基本功平时写业务时遇到数组嵌套多、数据结构复杂的情况多思考一步考试时自然就能写出来。5.4 大数相加与排序算法算法题通常不会太难但喜欢考“进阶版的基础操作”。比如大数相加JS 的Number最大安全整数是2^53 - 1超过这个范围精度就会丢失笔试让你用字符串实现两个大数相加function addBigNum(a, b) { let i a.length - 1; let j b.length - 1; let carry 0; let result ; while (i 0 || j 0 || carry 0) { const x i 0 ? Number(a[i]) : 0; const y j 0 ? Number(b[j]) : 0; const sum x y carry; result (sum % 10) result; carry Math.floor(sum / 10); i--; j--; } return result; }核心就是模拟竖式加法逐位相加并维护进位。排序算法里快排是最高频的写法代码不长但要练熟function quickSort(arr) { if (arr.length 1) return arr; const pivot arr[0]; const left []; const right []; for (let i 1; i arr.length; i) { arr[i] pivot ? left.push(arr[i]) : right.push(arr[i]); } return [...quickSort(left), pivot, ...quickSort(right)]; }如果笔试要求不额外分配新数组那就要写原地交换版本不过校招场景下这种递归版已经能拿大部分分数了。6. 备考思路与实战技巧6.1 复习优先级怎么排如果你准备时间有限我的建议是把精力按这个顺序分配JS 基础 浏览器与网络 Vue/React 原理 手写题 算法题。JS 基础是输出题、选择题、编程题全都绕不开的地基比如事件循环、作用域、闭包、Promise这四项是重中之重。浏览器和网络考的是概念的准确性花两个晚上集中背一遍缓存和渲染流程能拿到不少分。框架原理要靠平时积累临时抱佛脚读源码读不进去背一些白话版本的核心机制就够了。算法题每天刷个两三道重点覆盖字符串处理、数组操作、二分查找、二叉树遍历。6.2 别忽视代码输出题很多人复习笔试喜欢死磕算法题结果输出题翻车。输出题难在“看似简单处处是坑”一道题里可能同时包含变量提升、this指向、闭包和事件循环。比如这段var name global; function A() { this.name A; setTimeout(function () { console.log(this.name); }, 0); } var a new A();这题的输出是global不是A。因为setTimeout回调里的this指向全局对象普通函数不会继承外层this只有箭头函数才会。这个点是校招生最容易答错的。刷输出题时别只看解析在浏览器控制台里跑一遍把执行顺序用文字写出来形成自己的推理模板比刷十道新题都管用。6.3 手写题的高分策略手写题想要拿高分不只要跑通更要“好读、健壮、可扩展”。我总结一个三步骤流程第一步确认函数签名比如防抖函数接收什么参数、返回什么类型第二步处理边界条件比如空数组、null、循环引用第三步再考虑性能优化比如快排是否原地排序、深拷贝是否用 WeakMap 解决循环引用。很实用的小技巧是在写代码前先用注释把思路写出来// 1. 如果参数不合法直接返回 // 2. 用一个 Map 记录已经拷贝过的对象防止循环引用 // 3. 如果是 Date/RegExp单独处理 // 4. 遍历自身属性包括 Symbol递归拷贝这样阅卷人第一眼就能看到你的思考路径即使某行代码写错了也能根据注释判断你的思路是对的能挽回不少分数。笔试系统的在线编辑器没有自动补全很多同学平时用 IDE 习惯了考试时连querySelector都拼写不顺畅考前务必去牛客或者 LeetCode 的在线编辑环境练几道题提前适应。7. 常见问题与经验教训7.1 时间分配出问题怎么办校招笔试最常见的悲剧是选择题花太多时间纠结导致后面的编程题没写完。实际上编程题一道的分值可能顶得上五道选择题而且编程题只要按题目要求实现了功能正确率是确定的。我的策略是总体上“先做会做的再死磕不会的”。选择题遇到没把握的可以先按第一直觉选一个标记一下等编程题写完再回来改。笔试系统一般支持题号跳转别死脑筋按顺序一题一题磨。7.2 在线编辑环境的坑在线笔试的代码编辑器和 IDE 完全是两回事。没有自动补全、没有报错标红、没有 ESLint 提示甚至连缩进都要手动调。代码里少个分号、括号不匹配这些都只能靠你肉眼检查。我的经验是写完代码后做一次“口头跑查”用几个典型输入在脑袋里过一遍模拟每一步变量的变化。尤其是循环类题目手动跑三层循环就能发现索引边界错误这类问题。另外要注意缩进和变量命名平时写代码越规范考试时敲出来的代码就越整洁越容易自查。7.3 卷子没考但面试大概率会问这套卷子以笔试为主但笔试通过后还有至少两轮技术面试有些题笔试不考、面试必考。比如“讲一个你印象最深的项目说下技术难点”这题在笔试卷里很少出现却是面试开场保底题。建议提前准备一个真实的项目案例用“背景—方案—难点—效果”的结构讲清楚。另外像 HTTP2 和 HTTP3 的区别、浏览器标签页之间的通信方式、前端监控和错误日志上报机制这类偏实战的题校招越来越爱问。趁笔试刷题阶段把这些知识点一并整理成笔记面试时就能直接复用。写在最后说实话这套卷子给我最大的感受是它不考偏题怪题考的都是前端日常开发里一定会遇到的东西。变量提升也好、事件循环也好、缓存策略也好都是在真实业务中决定代码质量和排障效率的关键点。我当年自己做这套题的时候在事件循环输出题上翻了车原因就是当时只会背结论不理解任务队列的工作原理。如果你正在准备校招我真心建议不要只刷题把每个知识点背后的“为什么”搞明白。笔试只是入场券面试里追问的深度往往超出你的想象。把基础打扎实把原理吃透比背十套卷子都管用。这套卷子能帮你发现短板但真正拉开差距的是发现短板之后你是否愿意花时间补上。
返回列表