尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

摩拜2018前端笔试题复盘:核心能力模型从未改变

摩拜2018前端笔试题复盘:核心能力模型从未改变 最近后台收到好几条私信都在问同一件事手里有一份摩拜2018校招前端笔试卷想知道这种“过时”的题还有没有参考价值。我的回答是——太有了。前端面试这几年表面上看花样翻新从jQuery问到Vue 3、从Webpack问到Vite、从HTTP/1.1问到HTTP/3但你真正把一套2018年的卷子逐题拆开看会发现底层考察的能力模型几乎没变过JavaScript语言功底、浏览器渲染机制、算法基础、网络与安全、工程化思维。这也是我写这篇复盘文章的原因想借着这份试卷把前端笔试背后真正想筛选的东西讲透。这份试卷是典型的共享单车行业高速发展期的校招笔试题整体分成了几大块JS基础与异步、CSS布局与性能、算法与数据结构、网络与工程化外加一两道开放性的综合设计题。从题型分布能明显感觉到出题人不是在考“背了多少API”而是在考“有没有真正理解前端运行的本质”。下面我就按试卷的考察维度结合我自己的答题思路和后来的复盘总结一道题一道题地拆给你看。1. 试卷全景拆解出题人到底想筛什么样的人先说整体印象。这份试卷拿到手第一感觉是“基础题占比高但不白给”。整张卷子大概60%的分值集中在JavaScript语言本身包括闭包、this绑定、原型链、异步流程控制这些内容20%左右在CSS布局和浏览器渲染剩下20%分给算法、网络和工程化。这个比例在今天看依然合理甚至可以说越是核心的前端岗位JS基础题的占比只会越高。为什么是这样一个结构我当时和一个参与过校招出题的前辈聊过他的说法很直接校招生没有实际项目经验能拿来判断潜力的最主要依据就是基础是否扎实。而JavaScript作为前端唯一无可替代的编程语言它的掌握程度直接决定了这个人的天花板。你可以不会某个框架可以没写过大型项目但语言层面的东西必须形成肌肉记忆因为框架可以学语言基础补起来却非常慢。另外值得注意的一点是这套卷子几乎没出“背诵型”的题目比如“请列举Vue的生命周期”“React的setState是同步还是异步”这类。原因也很简单2018年前端框架生态已经进入多强并立的阶段Vue、React、Angular各有拥趸校招生用什么框架完全取决于实验室项目和实习经历出题人更倾向于考察技术内核而不是某一个框架的API细节。这一点到现在依然成立而且越来越明显。还有一类题目值得单独拿出来说就是“输出题”。所谓输出题就是给一段代码让你写出控制台的打印结果。这类题在卷子里占了好几分看似简单实际是区分度最高的题型。因为输出结果取决于你对变量提升、闭包、事件循环这些机制的综合理解任何一个环节不清楚答案就会差之千里。后面我会专门用一节来拆这种题。从整张卷子的难度曲线看出题人设置了很明显的送分题、拉分题和选做题。送分题保证大部分认真准备过的人都能答对拉分题把真正理解原理的人和只会背答案的人区分开选做题则是给极少数基础扎实且有扩展视野的同学留的加分空间。这种分层设计其实比很多堆砌难题怪题的试卷要科学得多也更值得后来的面试者去琢磨。2. JavaScript核心原理题闭包、this和异步的连环追问2.1 一道经典的“循环输出”题看看闭包到底有没有吃透卷子第一道大题就给了我一个下马威。题目大概是这样的for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); }问控制台最终输出什么。如果你脱口而出 0 1 2 3 4那这道题就丢分了。正确答案是 5 5 5 5 5。原因在于var声明的变量没有块级作用域for循环结束后i已经变成了5而setTimeout里的回调函数在1秒后才执行此时读取的i自然是全局作用域里那个已经变成5的i。接下来出题人很自然地追问那想让输出变成 0 1 2 3 4有哪些办法这就考察解决方案的积累了。最直接的方法是改用let声明因为let有块级作用域每次循环都会生成一个新的绑定。但要注意这里能成立的前提是setTimeout的回调形成了一个闭包捕获了每次迭代中的let变量。如果要刻意不用let还可以用 IIFE立即执行函数表达式包裹一层把当前的i作为参数传进函数形成参数副本。另外也可以把setTimeout改成“通过函数工厂返回一个新函数”的方式本质思路是一样的。这道题在今天面试中还经常出现说明它确实是考察闭包和异步的最佳载体。我后来在复盘时意识到闭包的核心不只是“函数可以访问外部变量”而是“这个外部变量被持久化保存了”。很多初学者能背出闭包的定义但一问“闭包会造成什么后果”就说不清了。比如闭包持有外部变量引用会导致这个变量无法被垃圾回收如果大量使用而没及时释放就会内存泄漏。这属于进阶考察点在笔试现场能主动写出来会给阅卷人留下很好的印象。2.2 this绑定的四种规则一条题全考完这份卷子的另一道题几乎把this的所有绑定规则都过了一遍。题目大概是这样var name window; var obj { name: obj, getName: function() { return this.name; } }; var getName obj.getName; obj.getName(); // 输出? getName(); // 输出?第一问obj.getName()输出obj因为函数作为对象的方法调用时this指向该对象。第二问把obj.getName赋值给一个变量再直接调用此时this指向全局对象非严格模式下所以输出window。我当年做这道题的时候第二问答错了因为当时没想明白“对象方法被提取出来后调用方式就变了”这个关键点。后来我才总结出规律this的指向不取决于函数定义在哪而取决于函数是怎么被调用的。这个规律到现在依然是面试高频考点而且经常和箭头函数放在一起考。箭头函数的一大特点是它不绑定this它的this继承自外层作用域。所以如果题目改成getName: () this.name那无论怎么调用输出都是window在浏览器非严格模式下。这个点经常有人记混我用一个生活化的类比帮助记忆普通函数就像一个“看人下菜碟”的社交达人谁请它吃饭它就听谁的箭头函数像个“认死理”的人它只认出生家庭也就是定义它时所在的作用域。2.3 Promise与事件循环把异步执行顺序画出来2018年的笔试卷上已经出现了 Promise 相关题目但当时V8引擎还不支持async/await的完整优化很多题还是围绕 Promise 的链式调用和微任务来出的。比如下面这道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。这里涉及的核心机制是事件循环的任务队列分级宏任务setTimeout和微任务Promise.then。JavaScript引擎在每轮事件循环中先执行一个宏任务然后把整个微任务队列清空再进入下一轮宏任务。因为 Promise 回调属于微任务优先级比定时器回调高所以会排在前面。我记得当时很多同学的误区在于以为setTimeout(0)是“立即执行”。其实浏览器对定时器的嵌套层级超过5层时最小延迟时间是4ms0并不等于立即执行。更深一层真实的浏览器事件循环还会区分“老的宏任务队列”和“新的宏任务队列”这在处理大量任务时会影响执行顺序。对笔试而言掌握“宏任务先进先出、微任务清空后再取下一个宏任务”这个规则就足够应对绝大多数题目了。我在复盘时给自己定了一个训练方法遇到任何异步题目先画一张时间线图把宏任务和微任务分别列成两个队列然后手动模拟每一轮事件循环的调度结果。练多了之后笔试里的异步题基本就是送分题。2.4 手写一个防抖函数分数差就在细节里这张卷子的JS题部分还有一道典型的手写题实现一个防抖函数。防抖的概念很简单——在事件被触发 n 秒后再执行回调如果 n 秒内又被触发则重新计时。但手写时很多人在细节上翻车。一个标准的防抖实现大概是function debounce(fn, delay) { let timer null; return function(...args) { const context this; clearTimeout(timer); timer setTimeout(() { fn.apply(context, args); }, delay); }; }这里有两个关键细节。第一this必须保留因为原函数可能被用作对象方法this指向错了运行直接报错第二事件参数args要传递进原函数否则回调里拿不到事件对象。我当时写的时候这两个细节都注意到了但没写clearTimeout(timer)里对timer的初始化处理会被判“在非严格模式下隐式创建全局变量”的不规范风险。这道题的加分项是能主动写出“立即执行版本”的防抖即第一次触发时立即执行之后 n 秒内的重复触发不执行并说明“防抖”和“节流”的使用场景差异防抖适合输入框实时搜索这类“只关心最终状态”的场景节流适合滚动监听、窗口调整这类“需要保持一定的执行频率”的场景。能把这些延伸内容写出来说明你不是背了一个函数模板而是真的理解了它的用途。3. CSS与浏览器渲染从盒模型到合成的完整链路3.1 盒模型不是背公式而是理解两种width的差异CSS部分的题目出题人很克制地没有考生僻属性而是回到了最基础的盒模型。一道题是box-sizing: border-box和content-box的区别以及在什么场景下优先用border-box。如果你只是回答“前者width包含padding和border后者不包含”那这道题只能拿一半分。另一半分数在于你能不能结合真实的页面布局说明为什么移动端优先用border-box。原因是设置百分比宽度后如果子元素还有padding或border在content-box下实际占用的总宽度会超出父容器导致布局溢出而border-box会把padding和border计算进width内无论你怎么加内边距总宽度都不会超过设定值省去手动calc的麻烦。这个知识点其实还牵扯出更深一层的问题浏览器的排版引擎在计算元素宽度时走的是“可用宽度 → 指定宽度 → 内容自动换行”这条链路。box-sizing只是改变了指定宽度的解读方式并不会改变“块级元素默认占满父容器宽度”的规则。好的答案会把这些推导逻辑写出来而不是干巴巴地背出两个属性值。3.2 需求驱动的布局题flex布局和浮动方案的权衡试卷的CSS部分还出了一道综合题给定一个经典的三栏布局左右固定宽度中间自适应要求写出至少两种实现方案。这道题考察的已经不只是某个属性而是你对布局方案演进的整体理解。最简单的方案是flex布局左右两栏设置固定flex-basis中间栏设置flex: 1。但要写全的话还得考虑父容器需要设置display: flex以及默认的flex-direction是row。另一个方案是使用浮动左右两栏分别float: left和float: right中间栏靠margin撑开。这种方案的坑在于中间栏的margin必须精确等于左栏和右栏的宽度之和而且这三个元素的DOM顺序有讲究必须把中间栏写在最后否则右栏会掉到下一行。我当时答的时候用了flex和float两种但在解析里忘了补充float方案需要清除浮动。后来想想这道题看似考布局其实是在考你有没有踩过真实布局的坑。只有真写过页面的人才会记得“用完浮动要清浮动”这种血泪教训。所以我在复盘里给自己提了个醒面试时遇到“你会做什么”的题一定要把“为什么会踩坑”也带出来这是区分“用过”和“会”的分水岭。3.3 重排与重绘性能优化题的必答内核CSS与渲染部分的最后一道题考得有点深度什么操作会触发重排Reflow和重绘Repaint两者有什么关系如何减少重排。这道题现在依然是前端性能优化里的必修内容。答案的核心在于浏览器在拿到CSS和DOM之后需要经过一个完整的渲染流水线布局计算Layout→ 绘制Paint→ 合成Composite。当你读取或修改某些属性时浏览器可能会强制提前执行一次布局以保证返回的数值是准确的这个过程叫强制同步布局。比如在循环里不断读取offsetTop、offsetHeight浏览器就得反复重新布局性能损耗极大。减少重排的核心思路是“批量操作”。比如先隐藏元素、改完样式后再显示或者用documentFragment来批量操作DOM。更进阶的做法是改变transform和opacity来触发合成层让浏览器跳过布局和绘制直接走合成阶段比如动画里的transform: translateX(100px)就比改left性能好得多。这道题在阅读器上已经涵盖了“浏览器工作原理”和“前端性能优化”两个方向所以笔试里能答得完整、有例子、有条理会在机器阅卷和人工资阅卷时都拿到不错的分数。我后来也经常跟刚入行的朋友说CSS不是“样式语言”而是“渲染优化语言”这个认知越早建立越好。4. 算法题难度不超LeetCode中等但边界条件是分水岭4.1 数组去重和字符串翻转都有“隐藏”考点算法部分的第一道题很常规实现一个数组去重函数。看起来简单但出题人限定了“不能用ES6的Set”这就逼着你去想别的方案。传统做法是双重循环时间复杂度 O(n²)。更好的做法是借助对象作为哈希表遍历一遍数组时间复杂度 O(n)。但这时候有一个坑如果数组元素包含对象或者NaN对象键的方式就会出问题。我当时写的是用filter配合indexOf代码很简洁但性能一般。后来看题解才发现最扎实的做法是用Map来做因为Map的 key 能区分NaN和对象引用比普通对象更稳妥。这个题目现在也经常出现它考的不是你能不能写出来而是你知不知道不同方案的边界差异。建议在准备这类题时主动把Set、Map、includes、filter、reduce这几种常见解法都列一遍并标注各自的适用场景和复杂度做到心里有数。另一道算法题是字符串翻转。题目基本限制不能用reverse()方法。此时考察的是双指针思路一头一尾两个指针交换字符然后向内移动。值得注意的边界条件是字符串长度是奇数是中间那个字符不需要交换while (left right)的条件要写对写成就会多交换一次。4.2 链表的反转递归和迭代两种写法都该会这套算法题里还有一道“反转链表”这在2018年的前端笔试中算是中等偏上难度。前端虽然平时用链表不多但链表的题目能考察候选人对“引用”和“指针”的理解而这恰好是JavaScript对象引用的逻辑延伸。迭代写法的核心是三个指针的配合prev、current、nextTemp。每轮循环里先保存current.next再把current.next指向prev然后prev和current同时向前移动一步。很多人在这里容易乱其实口诀是“先存后继再改指向最后移动”。递归写法则更精妙先递归到链表末尾再一层层把方向指回来。递归写法在面试中更容易拿满印象分但要注意递归深度的问题——在链表非常长时递归写法很容易导致栈溢出所以在实际工程中更推荐迭代写法。4.3 结合业务场景的算法题共享单车锁车位置怎么找最有意思的一道算法题出题人把业务场景融了进去有一批共享单车的位置坐标给定一个用户的位置坐标要求找出距离最近的N辆车。这题的核心思路是把所有车的位置和用户位置的距离都算出来然后排序取前N个。如果数据量小直接全量算然后排序如果数据量很大就得用堆排序维护一个大小为N的最小堆只保留当前距离最小的N辆车时间复杂度能从 O(n log n) 优化到 O(n log N)。这道题的考点显然不只是排序算法本身而是你能不能把“业务需求”翻译成“算法模型”。我在当时写出了全量计算加排序的版本但没写出堆优化方案只拿了一半分。后来复盘时意识到“找最近的N个”是一个高频的top-N问题类似的需求还有“热门搜索词Top10”“评分最高的电影Top5”这类问题的最优解就是用堆。这个认知对我来说比一道题的分数重要得多它让我在后续的面试里遇到类似问题时有了迁移能力。5. 网络与工程化决定你能不能进二面的隐藏分水岭5.1 HTTP缓存的两大思路强缓存和协商缓存的应用时机网络部分的第一道题就考了HTTP缓存。分值不小而且问得很细强缓存和协商缓存的区别分别对应的响应头字段是什么以及为什么要有这两种缓存策略。强缓存对应的是Expires和Cache-Control它表示“这个资源在过期之前浏览器根本不会发请求直接用本地副本”。Expires是HTTP/1.0时代的字段用绝对时间表示但如果本地时间和服务器时间不一致就会失效Cache-Control是HTTP/1.1引入的相对时间字段比如max-age3600表示从获取资源起的一小时内有效不会受本地时间影响所以更可靠。协商缓存则是“发请求问服务器这个资源变没变”没变就返回304变了就返回新资源。对应的字段是Last-Modified/If-Modified-Since和ETag/If-None-Match。ETag是用实体内容生成的一个唯一标识如果文件内容变了ETag就变这种方式的精度比单纯依赖时间戳更高是主流做法。这道题的回答如果能联系到实际开发中的静态资源处理比如文件名带hash值会更出彩因为这说明你不只是会背书还知道缓存是工程的一部分。5.2 安全题XSS和CSRF的攻防姿态网络安全部分有一道题是基础的“XSS与CSRF的区别”。这在2018年的笔试卷里是拉分项因为很多前端同学完全没有安全意识。XSS跨站脚本攻击的核心是“恶意脚本被注入到页面中执行”防御重点是“不信任用户的输入”需要做转义处理CSRF跨站请求伪造的核心是“诱导用户在已登录的站点上执行非本意的操作”防御重点是“验证请求来源”比如加Token校验。这道题从今天回看其实能拉开一道更大的问题前端的边界在哪里安全是后端的事还是前后端共同的事好的回答会说明这两类攻击都需要前端后端配合防御前端可以通过 CSP内容安全策略、输入转义、敏感操作二次验证来降低风险后端则需要校验Origin、使用CSRF Token。一个前端候选人如果能主动说出“CSP”这个头部字段说明你研究过浏览器安全策略这在2018年的校招中已经属于亮眼表现放到今天更是基础分。5.3 工程化题Webpack的构建流程和Loader的工作方式工程化部分的题目在2018年已经出现但当时很多院校还没开相关课程所以区分度非常高。一道题是简单描述Webpack的构建流程以及Loader和Plugin的关系。Webpack的构建流程可以浓缩成几个步骤从入口文件开始递归解析模块依赖并把所有模块交给对应的Loader处理比如把ES6语法转成ES5然后经过一些Plugin的介入比如压缩代码、生成HTML最后打包成能在浏览器中运行的文件。Loader是“在单个文件层面做转换”它处理的是文件内容Plugin是“在整个构建流程层面的扩展”它监听构建的生命周期事件可以做更复杂的操作比如HtmlWebpackPlugin在生成HTML时动态注入打包后的脚本路径。我当时在笔试里把这个流程写了但没有提到“构建依赖图”这个核心概念。后来回顾时才发现Webpack的本质是构建模块依赖图这个图记录了每个模块之间的关系最终按图生成chunk。能答到“依赖图”这个层面说明你不是在背Loader列表而是真正理解了打包工具的设计思想。6. 笔试现场的真实答题节奏与控制技巧6.1 时间分配前面的基础题最多花40%的时间整张卷子的答题时间是90分钟。我的经验是无论题量多少时间分配一定要在拿到卷子的第一分钟就做好。基础题JS和CSS一般占分高且好拿优先保证正确率建议控制在40分钟以内。接下来是算法题因为算法题即使思路对也要花时间写代码建议给它30分钟左右。最后把20分钟留给网络、工程化和开放题。我见过太多同学在做基础题时反复纠结一道“输出题”写了又划掉结果到最后算法题只剩10分钟根本没时间思考。实际上输出题的答案往往第一步就定下来了你只需花2分钟检查一遍就果断往后走。6.2 手写代码时阅卷人最在意的三个细节手写代码题是整套卷子的核心得分项但我发现三个细节被很多人忽略。第一变量命名。a、b、tmp不是不行但如果能写prevNode、currentNode这种一看就明白的语义化命名会给阅卷人一种“这个人是码农思维”的感觉。第二边界条件。哪怕你的主体逻辑写得很顺如果函数开头没有处理空数组、空字符串、长度为0这些边界情况这道题大概率会被扣掉步骤分。第三是必要的注释。不是让你每行都写注释而是在关键步骤比如“这里用一个对象来去重”旁边写上一句说明能大大提升沟通效率。6.3 遇到不会的题怎么办拿到步骤分的策略校招笔试有一个特点题目之间往往有渐进关系。前面的小问是为了后面的大题做铺垫如果你能答出第一问哪怕不会第二问也一定要把第一问的结论写上。比如试卷里有一道题先让写“防抖函数”第二问让写“防抖的立即执行版本”——我第一问拿全分后第二问只写了一行注释“这里应该在触发边界执行一次性回调”也能捞到一两分。阅卷人看的是“思路有没有”而不是“答案完不完整”。我自己的体会是笔试不像面试面试可以补救笔试写下去就定分了。所以“做完整比做完美更重要”这句话在校招笔试里尤其适用。哪怕代码有瑕疵只要整体思路清晰步骤齐全阅卷老师很容易给到接近满分的分数。6.4 考前模拟用“真题限时手写输出”的方式找手感最后分享一个我后来经常给学弟学妹的备考方法不要只看题要限时做并且用手写的方式输出代码。原因很现实笔试环境不允许你用IDE的自动补全、语法提示甚至不允许你反复执行调试。手写代码时你才会发现自己对 ES6 的解构、Array.prototype.reduce的回调参数、let和const的块级作用域这些基础语法掌握得是否够扎实。我当时把这张摩拜卷子从头到尾做了一遍限时90分钟不准查资料做完再对着答案逐题分析。整个过程非常痛苦因为我发现即使自己平时写了很多项目一旦脱离IDE很多方法的参数都记不清了。但正是这种“痛苦”帮我补上了知识盲区后来在同年的其他几家大厂笔试中我明显感觉更从容。提示如果你也打算用类似方式备考建议找一个完全安静的环境用纸和笔作答然后用手机拍照录入答案。这个流程能最大程度模拟真实的“代码手写”场景。现在回看这份摩拜2018校招前端笔试卷我发现它最大的价值不在于题目本身有多难而在于它如实反映了一家公司对前端校招生的能力预期。这份预期在2026年的今天依然适用JavaScript语言基础、浏览器渲染机制、算法与数据结构、网络与安全、工程化思维这五座大山一座都绕不过去。与其在网上到处搜“2026最新前端面试题”的合集不如静下心来把一套有代表性的真题研究透把背后的原理彻底弄明白。毕竟面试题的形制会变面试官想考察的核心能力不会变。
返回列表