1. 整体题型与考点拆解:这份笔试题在考什么
2019年小米秋招前端笔试题(A)传出后,很多准备面试的同学第一反应是“题目很多、范围很杂”,但我刷完几遍之后反而觉得,这份卷子的出题思路其实非常清晰——它想筛选的不是“背过多少API”的人,而是“有没有真正写过前端、理解过浏览器、踩过兼容性坑”的人。整份卷子大约包含了选择、填空、简答和手写代码四类大题,覆盖了JavaScript核心机制、HTML/CSS布局、浏览器原理、网络基础、框架使用和少量算法逻辑。从分值分布看,JavaScript相关内容的占比达到了四成以上,其次是CSS布局与浏览器渲染相关的题,框架和工程化内容占比并不高,这说明小米前端团队在2019年这个时间节点上,更看重候选人的原生功底,而不是“会哪个框架”。
这一点和当时很多大厂的出题方向是一致的。2019年前端圈正处于Vue和React二分天下的阶段,各种脚手架工具已经把工程化门槛降得很低,很多候选人简历上写着“熟练掌握Vue全家桶”,但一碰到“Vue的响应式原理到底是什么”“computed和watch的执行时机区别”这类问题就答不上来。小米这份卷子明显在刻意规避这种“框架熟练度幻觉”,把大量题目压在语言本身和浏览器机制上,目的就是筛掉那些只会用框架、不懂底层的人。说白了,框架可以进公司再学,但JavaScript和浏览器原理不行,这是前端这个岗位的立身之本,也是衡量一个人能不能解决线上疑难杂症的关键。
从难度梯度来看,这份卷子也做得比较合理。前面选择和填空题大多属于“知道就能答对”的基础题,例如事件冒泡与捕获的顺序、==和===的区别、基本数据类型的判断等,这部分是给候选人热身用的,也是给面试官建立初步能力画像用的。中间部分的简答题开始考察理解深度,比如“解释一下执行上下文”“描述从输入URL到页面展示的完整过程”,这类题没有标准答案,却最能区分“背过八股文”和“真正理解”的候选人。最后的手写代码题则是拉分项,常见的有防抖节流、深拷贝、数组去重、Promise的简单实现等,题目本身不偏不怪,但要在有限时间内写得严谨、考虑边界情况,没有扎实功底很容易翻车。
我刷完这份卷子之后最大的感受是:它和后来几年很多公司出的“八股文刷题库”有本质区别。八股文题库往往堆砌冷门API和偏题怪题,候选人靠刷题量就能堆出高分。而小米这份卷子的题目几乎全部来自日常开发中真正会遇到的问题,只是换了一层考察包装。换句话说,如果你平时写代码时习惯“知其然也知其所以然”,不用刻意准备也能拿到不错的分数;反之,如果只是机械地调框架API、复制粘贴解决方案,即使刷了一百套题,遇到这份卷子依然会露馅。
2. JavaScript核心机制题详解:语言功底是分水岭
2.1 原型链与继承:最常见的“送命题”
小米这份卷子里,原型链相关的题几乎是必考的,而且考察方式很刁钻,不是让你背“原型链是什么”,而是给你一段代码,让你写出输出结果。例如有这样一道高频题:
function Foo() { this.name = 'foo'; } Foo.prototype.say = function() { console.log('I am ' + this.name); }; function Bar() { Foo.call(this); this.age = 18; } Bar.prototype = Object.create(Foo.prototype); Bar.prototype.constructor = Bar; var b = new Bar(); b.say();这道题考察的核心是构造函数、原型对象、实例三者之间的关系,以及组合继承的实现方式。正确答案是输出“I am foo”。但很多候选人会在这里犯迷糊,原因在于对Object.create(Foo.prototype)这一步理解不到位,以为这就完成了继承,却没有意识到如果直接用Bar.prototype = Foo.prototype,两个构造函数的原型会指向同一个对象,后面给Bar.prototype添加方法时,Foo.prototype也会被污染。我当时在给候选人做模拟面试时,至少有一半的人会踩这个坑。
另外还有一道让我印象深刻的题目,也是原型链里的经典变体:
var obj = {}; console.log(obj.__proto__ === Object.prototype); console.log(Object.prototype.__proto__); console.log(Function.prototype.__proto__ === Object.prototype); console.log(Object instanceof Function); console.log(Function instanceof Object);这里面的前两个输出相对简单,obj.__proto__指向Object.prototype,而Object.prototype的__proto__是null,因为它是原型链的终点。关键是后面两个,Object instanceof Function是true,Function instanceof Object也是true,因为Object本身是构造函数,而构造函数本质上是Function的实例,同时Function.prototype的原型又指向Object.prototype。这种“鸡生蛋蛋生鸡”的关系很多候选人第一次接触时都会懵,但真正理解了JavaScript的对象模型之后就非常清晰了。我的建议是画一张原型链图,把Object、Function、Array、String这些内置构造函数和它们的prototype、__proto__关系画出来,比死记硬背效果好得多。
2.2 闭包与作用域:输出题重灾区
闭包这块,小米的卷子基本不考概念题,全部是代码输出题。有一道非常经典的题是这样的:
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 100); }输出结果是5个5,这一点大多数人都知道,所以它不算难点。真正的分水岭在于后面这问:“如果我想输出0、1、2、3、4,有几种改法?”这就考察了候选人是不是真的理解闭包、块级作用域和立即执行函数之间的关系。常见的解法有三种:把var改成let,利用块级作用域绑定循环变量;用IIFE包裹一层,将i作为参数传入;或者在setTimeout外面包一层函数并通过参数传递。这三种解法背后的原理并不完全相同,let的方式依赖于词法环境,IIFE的方式依赖于函数作用域,但本质上都是在制造一个独立的变量环境来保存每次循环的i值。
小米这份卷子还特别喜欢考一道和闭包相关的工厂函数题,大意是:
function add(x) { return function(y) { return x + y; }; } var add5 = add(5); console.log(add5(3)); console.log(add(2)(4));这道题本身不复杂,输出8和6,但它后面经常跟着一道变体,考察闭包中变量引用的陷阱:
function fn() { var arr = []; for (var i = 0; i < 3; i++) { arr.push(function() { return i; }); } return arr; } var result = fn(); console.log(result[0]()); console.log(result[1]()); console.log(result[2]());这里三个输出全是3,因为arr里的三个函数引用的都是同一个i,当函数执行时i已经结束循环变成3了。这道题比前面的setTimeout更隐蔽,因为很多候选人会以为数组里每个函数保存了当时i的副本,实际上保存的是对同一个变量的引用。我在实际开发中也遇到过类似问题,比如在循环里绑定事件处理器,如果不注意闭包陷阱,点击每个元素时拿到的都是最后一个索引,所以这个考察点非常贴近实战。
2.3 异步与事件循环:必考且容易翻车
事件循环是2019年前端面试的绝对热门,小米的卷子里自然也少不了。一道典型的选择题是问下面代码的输出顺序:
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即使延迟为0,也会被放入宏任务队列,等到下一轮事件循环才会执行。很多候选人能答对这道题,但一旦把题目改成嵌套的setTimeout和Promise混合体,或者加上async/await,就彻底乱了。
有一道让我印象特别深的变体题,把async也卷了进来:
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); async1(); setTimeout(function() { console.log('setTimeout'); }, 0); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end');这道题的输出顺序是:script start、async1 start、async2、promise1、script end、async1 end、promise2、setTimeout。关键在于await表达式会先把后面的async2()同步执行完,然后在await处让出线程,将后续代码作为微任务注册。这里很多人会把async1 end和promise2的顺序搞反,实际上await后面的代码比普通Promise.then注册得更早,所以async1 end先于promise2输出。这类题一旦把async/await加进来,考察的就不只是概念记忆,而是对事件循环执行机制的真正理解。我的建议是不要死记输出顺序,而是在浏览器控制台里多跑几遍,配合debugger断点观察调用栈和任务队列的变化,理解才会更深刻。
3. HTML/CSS与浏览器原理:布局和渲染是基本功
3.1 盒模型与布局题:看似基础实则陷阱重重
CSS部分的考察重点首先是盒模型。小米这份卷子考了一道很经典的题:一个宽度为200px的元素,设置了padding: 20px和border: 5px solid,在不设置box-sizing的情况下,它实际占用的宽度是多少?答案是250px,因为默认的content-box模式下,宽度只计算内容区,padding和border会向外扩展。但如果设置了box-sizing: border-box,则元素总宽度保持200px,内容区自动压缩。这道题对熟悉CSS的人来说是送分题,但我在实际评卷时发现,有不少候选人会把box-sizing的默认值记反,说明他们平时写样式时依赖UI框架的reset.css太多,没有真正理解盒模型的计算逻辑。
另一个常考的是垂直居中问题。在2019年的出题背景下,答案其实已经比较成熟:flex布局的align-items: center和justify-content: center组合是最推荐的方案,position + transform是兼容性更稳妥的古老方案,display: table-cell和line-height方案只适用于特定场景。小米这份卷子考的是“请列举三种垂直居中的方式,并说明各有什么优缺点”,这其实是一道很实际的工程题,因为在真实项目中,不同场景下适合的居中方完全不同——如果父容器高度不固定,line-height方案就行不通;如果涉及到弹窗居中且需要兼容IE9,flex可能就不太好使。候选人能否结合具体场景说清楚方案取舍,远比背出三种方案更有价值。
布局方面,小米还考了一道BFC相关的简答题,问“如何触发BFC,以及BFC能解决什么问题”。我当时看到这道题时觉得很亲切,因为BFC(块级格式化上下文)确实是CSS里最实用也最容易被忽视的概念之一。常用的触发方式包括overflow: hidden、float、position: absolute、display: inline-block、display: flex等。BFC能解决的典型问题有:清除浮动带来的父容器高度塌陷、防止上下margin合并、阻止元素被浮动元素覆盖等。候选人如果能从“什么是格式化上下文”讲到“为什么BFC能隔离内部元素对外部的影响”,再配合一两个自己在实战中遇到的案例,这道题就能答得很出彩。
3.2 浏览器渲染机制与性能优化题:不止是背流程
浏览器输入URL到页面展示的完整过程,几乎是所有前端笔试的必考题,小米这份卷子也不例外。完整答案包括DNS解析、建立TCP连接、发送HTTP请求、服务器响应、浏览器解析HTML构建DOM树、解析CSS构建CSSOM树、将两者合并为渲染树、计算布局、绘制页面等步骤。这道题看起来是背诵题,但实际上如果候选人只是背流程,而没有一个环节能展开讲,面试官很快就能识破。比如问到“DOMContentLoaded和load事件有什么区别”,如果只回答“前者是DOM解析完触发,后者是图片等资源加载完触发”,那只算及格;如果能进一步说明“如果脚本使用defer或async加载,DOMContentLoaded的触发时机会有不同表现”,并解释defer保证执行顺序、async不保证,这才算真正理解了浏览器解析机制。
小米这份卷子里还有一道让我觉得出得很好的题:给出一段包含script标签、样式表和内联脚本的HTML片段,问页面的渲染会如何被阻塞,以及如何优化。这道题表面考的是“脚本放在head里会阻塞渲染”这个基础点,但往深了问就会涉及到async和defer的区别、preload和prefetch的适用场景、CSS是否会阻塞DOM解析等细节。我在实际优化过的项目中就踩过类似的坑:一个首屏页面引了一个第三方统计脚本,放在head里同步加载,结果导致整个首屏白屏时间多了将近800毫秒。后来改成async加载,首屏渲染速度立竿见影。这种经验如果能写进答题里,面试官会非常认可。
还有一道关于CSS性能优化的题也值得一提:请说说重排(reflow)和重绘(repaint)的区别,以及如何减少重排。这道题的答案要点在于:重排是指布局引擎需要重新计算元素的位置和大小,代价远高于只更新像素的重绘;常见的触发重排的操作包括修改元素宽高、增删DOM节点、改变窗口尺寸、读取offsetHeight等属性;优化手段包括使用transform代替top/left做动画、批量修改DOM后再统一插入、用DocumentFragment减少DOM操作、避免频繁读取布局属性等。我当时的建议是,如果时间充裕,可以现场写一段代码示例,演示如何通过合并读写操作来减少强制同步布局,这种实操展示比纯文字回答更有说服力。
4. 算法与手写代码题实战:考察编程思维和代码习惯
4.1 算法基础题分析:偏向逻辑思维而非竞赛难度
小米这份试卷的算法题整体难度低于纯竞赛类题目,更偏向于考察基础逻辑和编程思维,这和大厂前端岗位的定位是一致的——前端工程师不需要会做红黑树手写,但必须能把一个实际问题抽象成算法模型并落地实现。典型题目包括数组去重、字符串反转、斐波那契数列、二分查找等。比如数组去重这道题,看起来简单到所有人都会写,但考察点在于你能写出多少种解法,以及是否知道各种解法的适用场景。最基础的解法是双重循环去重,时间复杂度O(n²),只适用于小数组;进阶一点的用indexOf或includes判断,代码更简洁但依然是O(n²);再往上是利用Object或Map做哈希表去重,时间复杂度降到O(n),但需要注意Object的键会被转成字符串;最优雅的是利用Set的特性一行搞定。
我当时在实际开发中遇到过一个大数组去重的性能问题,数组长度接近十万,里面包含大量对象,用includes去重跑了近一秒。换成Map按对象的id字段去重后,耗时降到了数十毫秒。这个案例放在笔试题的答案里,会让人觉得你是真正处理过性能问题的人,而不是只会背解法。小米的题还喜欢在去重基础上加条件,比如“只对数字类型去重,保留第一次出现的顺序”,这就排除了Set和Object的一些便捷方案,考察候选人能否灵活应对约束条件。
还有一道字符串相关的题也值得说说:实现一个函数,判断一个字符串是不是回文串。基础解法是双指针从两端向中间遍历,时间复杂度O(n),空间复杂度O(1)。这道题本身不难,但小米后续的追问是:“如果字符串里包含空格和标点符号,忽略这些字符再判断,应该怎么处理?”这实际上是在考察字符串预处理和正则表达式的熟练度。候选人如果能在解题时主动考虑到这些边界情况,说明代码思维比较成熟,而不只是会写教科书答案。
4.2 手写代码题:防抖、深拷贝和Promise是重头戏
手写代码题是整份试卷中最能拉差距的部分。小米2019年的笔试题中,防抖和节流的实现几乎是必考的。这两个概念看似简单,但很多候选人写着写着就暴露了对this指向和参数传递理解的不足。一个标准防抖函数的实现是:
function debounce(fn, delay) { let timer = null; return function(...args) { const context = this; if (timer) { clearTimeout(timer); } timer = setTimeout(() => { fn.apply(context, args); }, delay); }; }这里的关键点在于:返回的函数必须保留this指向,否则在作为对象方法使用时会出现this丢失;...args用来透传原函数的参数;timer保存在闭包中,实现“下次调用会取消上次的定时器”的效果。很多候选人能写出主体逻辑,但容易漏掉this的处理或参数的透传,这两点恰恰是在真实业务中避免bug的关键。节流函数同理,区别在于节流是保证在一段时间内只执行一次,实现方式有定时器版和时间戳版两种,各自在处理首尾触发时的行为不同,候选人如果能把两种版本的差异说清楚,得分会高很多。
深拷贝也是小米卷子里的常客。这道题看着不难,但“深搜到底”并不容易。一个合格的深拷贝需要处理:基本类型直接返回、数组和对象的递归复制、循环引用导致爆栈、特殊对象如Date、RegExp、Map、Set的兼容等。我在评卷时看到过很多版本,有的只针对普通对象做递归,遇到数组就出问题;有的完全不处理循环引用,测试时直接栈溢出。一个相对完整的实现思路应该是:先用WeakMap存储已拷贝的对象,遇到循环引用时直接返回缓存引用,然后判断属性类型分别处理。小米这道题还有一个有趣的加分点,是提示候选人能不能用JSON.parse(JSON.stringify(obj))来实现深拷贝,以及该方法的局限性是什么——不能拷贝函数、不能处理undefined和Symbol、不能处理循环引用、会丢失对象的constructor等。能答出这些局限性并说明自己实现方案时如何规避,这道题就能拿到高分。
Promise的手写实现是另一道重量级题目,不过2019年小米的卷子没有要求完整实现Promise的整个规范,而是考了一个简化版的链式调用和异步执行。但即使如此,候选人需要对Promise的状态机(pending、fulfilled、rejected)和执行流程有清晰理解才能真正写对。我建议准备这道题时至少要实现resolve、reject、then的核心逻辑,并且注意then返回的必须是一个新的Promise,才能支持链式调用;回调的执行需要放进微任务队列,在浏览器环境下可以用Promise.resolve()来模拟微任务。
5. 框架题与工程化方向:考察深入但不偏门
5.1 框架原理题:Vue和React的底层对比
2019年小米的笔试里,框架相关题目占比不高,但考察的方向很明确。Vue的题主要集中在响应式原理上,比如“Vue 2.x是如何实现数据响应式的”,标准答案是Object.defineProperty对数据对象的每个属性进行劫持,在getter中收集依赖、在setter中触发更新。进阶追问是“为什么Vue 3要改用Proxy”,答案要点包括:Proxy可以拦截对象级别的操作而不需要遍历对象的每个属性、支持动态新增属性和删除属性的响应式、性能更好、可以拦截数组索引操作等。候选人如果平时只看文档不用源码,很难把这道题讲得太深,所以它能有效区分“用过Vue”和“理解Vue”的人。
React的题目则更多集中在组件生命周期和状态管理上。2019年时React 16.8刚推出来不久,Hooks是热门话题。小米考了一道“请对比useEffect和componentDidMount的差异”,这其实是一道很有深度的题,因为useEffect在每次渲染后都会执行(可以传入依赖数组控制),而componentDidMount只在组件挂载后执行一次;useEffect的清理机制对应的是componentWillUnmount;useEffect的执行时机在浏览器绘制之后,而componentDidMount在绘制之前触发。能把这些细节梳理清楚,说明候选人不是只写过几个Demo,而是踩过Hooks闭包陷阱、想过执行时机的真实问题。
5.2 工程化题:打包工具和代码规范离不开
工程化方面的题目,小米考得比较务实。有一道题是问“Webpack的loader和plugin有什么区别”,这是前端构建领域最基础的考题。答案要点是:loader是文件加载器,负责将不同类型的文件转换成模块,本质上是导出函数的处理器;plugin是插件,可以介入Webpack生命周期的任意阶段,执行更复杂的构建优化任务。候选人如果答到这里就停,只能算是及格;如果能进一步结合自己使用过的babel-loader、file-loader、html-webpack-plugin、mini-css-extract-plugin等工具,说明各自解决了什么问题,并分享一个通过配置插件完成构建优化的实际案例,这道题的得分会明显高出平均水平。
代码规范也是小米考察的一个方向,有一道题是关于ESLint配置的作用和必要性。这体现了公司对团队协作质量的要求——前端项目多人协作时,如果没有统一的代码规范,代码风格会变得杂乱无章,code review的成本会直线上升。候选人如果能在答案中提到自己参与过团队ESLint规则配置的经验,比如根据项目实际情况关闭某些过于严格的规则、集成prettier统一格式化风格、在CI流程中加入lint检查拦截不合格代码等,会给面试官留下“有团队协作经验”的印象。我当时在准备这类题目时,把平时在项目中顺手配置过的工具都翻出来复盘了一遍,发现很多当时觉得“理所当然”的配置,正是因为理解了背后的原理,才能在笔试中举出真实案例。
6. 高频失误与备考建议:那些年我们一起踩过的坑
6.1 笔试现场的高频错误清单
根据我刷小米2019年秋招笔试题的经验,结合周围朋友的反馈,整理出几个在笔试现场最容易出现的典型失误:
第一类是时间分配失衡。前面选择和填空题占据了大量时间,导致最后的手写代码题没时间仔细思考。小米的试卷题量不小,前面的基础题确实相对简单,但正因为简单,很多候选人会反复斟酌、想着“确保万无一失”,结果等到后面的简答题和代码题时已经剩下不到二十分钟。我的建议是遇到一眼能看出答案的选择题就快速跳过,拿不准的先标记题号,等全部写完后再回头思考,千万不要在前面跟题目死磕。
第二类是手写代码时忽略边界条件。很多候选人写深拷贝时忘了处理循环引用,写防抖函数时忘了保留this指向,写数组去重时没考虑到NaN的情况。这些细节在本地IDE里运行时编译器会提示错误,但笔试时是白板或在线编辑器,不会给你实时调试的机会,只能靠平时的代码习惯来规避。我建议备考时尽量手写代码,不要依赖IDE的自动补全和报错提示,逼自己在写代码时就考虑所有边界情况。
第三类是概念回答“只见树木不见森林”。比如问到“什么是闭包”,如果回答“函数内部定义函数,内部函数可以访问外部函数的变量”,这只是给出了定义;如果能进一步说明闭包的形成机制、内存泄漏风险和应用场景(如防抖节流、私有变量、模块化),才能展现真正的理解深度。小米的阅卷人大概率是前端团队的资深工程师,他们评分时最看重的就是候选人能否把概念和实际开发经验结合起来。
6.2 如何针对性准备小米这类笔试题
结合小米这份卷子的出题风格,我给准备大厂前端笔试的同学几条具体建议:
第一,用“输出题”敲打基础概念。不要只看JavaScript教程,而是找大量的代码输出题来练手,尤其是闭包、事件循环、原型链这三个高频考点的变体题。每做一道题,不仅要写出正确答案,还要把每一步的执行过程口头讲清楚,能用“这段代码为什么输出这个结果”来解释,才算真正掌握。
第二,手写代码不能只看不练。防抖节流、深拷贝、数组去重、字符串模板解析、Promise简化实现、EventEmitter这些常见手写题,建议至少完整地将代码在纸上敲三遍以上,第一遍凭记忆写,第二遍看答案纠错,第三遍不看答案独立写并主动扩展边界情况。我在备考时还发现一个有用的技巧:把自己写好的手写代码放到LeetCode或Codewars上跑一些测试用例,因为笔试的在线评测系统通常会在用例里埋一些边界坑,提前见识过会安心很多。
第三,CSS的重点要落在“能解释为什么”上。盒模型、BFC、垂直居中这些基础知识点,大家都能说上一两句,但面试官更想听到的是你在不同场景下的选择理由。准备时可以先列出常见布局场景,再针对每种场景比较不同实现方案的优劣,形成自己的决策逻辑,而不是死记硬背标准答案。
第四,不要忽略浏览器原理和网络基础。小米的试卷里,从URL输入到页面渲染的题目反复出现,它本质上是在考察候选人对“前端代码如何在浏览器中变成用户看到的页面”这个完整链路是否有清晰的认知。建议把整个流程画成一张图,把每一步涉及的关键概念(DNS、TCP、HTTP缓存、DOM树、CSSOM、渲染树、重排重绘)串联起来,能用自己的话把整条链路讲清楚,这类题目基本就不会失分了。
6.3 笔试过后的复盘思维
我见过太多候选人考完就把试卷丢到一边,只关心“过没过”,而不关心“错在哪”。这种做法浪费了笔试最有价值的部分——它其实是一次免费的、由大厂前端团队出题的能力体检报告。每道错题背后,都对应着一个知识盲区或思维盲点。正确的复盘姿势是:考完后把每道题重新做一遍,标出哪些是“本来会但粗心错了”,哪些是“根本不会纯靠蒙”,哪些是“会但答得不完整”的。粗心错的需要在后续做题中提高警惕;不会的题需要回归教材补基础;答得不完整的题则说明理解深度不够,需要多问几个“为什么”来深化。
另外,我建议大家养成整理错题集的习惯。前端知识点很零碎,今天看过的原型链题,明天可能就被事件循环的题挤到脑后了。用笔记工具把做错的题、踩过的坑、容易混淆的概念集中记录下来,每隔两周回顾一遍,效果比每天刷一百道新题要好得多。我在备考期间维护了一个“前端面试错题本”,里面记录的全是自己做错的或理解不到位的题目,每道题下面还有当时的错误原因分析和正确思路笔记。后来面试时,我发现很多考点的出题角度和我错题本上记录的几乎一样,当时内心真的感慨:“果然所有大厂的坑都是相似的。”
这份小米2019年的前端笔试题,放到几年后的今天来看有些知识点已经过时了,比如手写XMLHttpRequest的题现在已经被fetch取代了一大半,Vue 2的响应式原理也被Vue 3的Proxy方案更新了,但它的核心考察逻辑——不求偏、不求怪,只求候选人真的理解前端最基础的那块基石——放到任何一届秋招都不过时。准备笔试题的过程,本质上就是把过去几年写过的代码重新从底层审视一遍,而这个审视的过程,可能就是技术水平上一个台阶最好的契机。