每年到了三四月份,就是前端校招笔试最密集的时候。我当年也做过蘑菇街2019届的实习生笔试题,后来带团队做前端面试官时也反复把这份卷子翻出来讲给组里的人听。说实话,这份卷子的出题水平放在今天来看依然很在线,基础题和工程实践题的配比恰到好处,既能筛掉没写过代码的人,也能把只会背八股文的同学卡在半路。这篇文章就把这份笔试题逐点拆开,结合我当时做题的经验和后来面试别人的视角,把考点、答法、以及考完应该怎么复盘都聊透。
如果你是准备投电商方向前端岗位的在校生,或者刚转行想摸清公司笔试套路的同学,这篇内容基本可以当一份带答案的题型地图用。我尽量不写教科书式的原理解释,多讲“这题到底在考什么”“为什么这么出”“当时我应该怎么写才能多拿分”。
1. 蘑菇街校招前端的考题定位:它在问什么
1.1 电商业务对前端能力的需求画像
先看蘑菇街本身。蘑菇街是以时尚电商为核心业务,它的前端业务场景有几个很突出的特点:页面流量大、营销玩法多、业务节奏快、精细化运营要求高。这就决定了它的前端团队在选人时,不只要看你有没有写过网页,更要看你能不能适应电商业务的真实环境。
笔试题目里大量出现的事件绑定、数组去重、ajax请求顺序控制、移动端适配,都是电商业务里每天都要碰的东西。比如首页的秒杀倒计时、商品列表的加载更多、购物车的全选反选、订单页的状态切换,这些交互对应的就是DOM操作和事件处理。而在营销页里经常要做倒计时和定时轮播,这就直接考到定时器闭包和异步执行顺序。所以不要把这套题当成普通的知识点考试,它是带着明确业务意图的筛选工具。
另外,蘑菇街本身就是移动端占比很高的平台,所以CSS适配和移动端交互的题目占比不小。如果你看完题目觉得“这不就是我们平时做详情页会遇到的问题吗”,说明你对电商前端的工作场景已经有了基本的体感。
1.2 笔试题型结构与时间分配分析
这份卷子的结构我不完全记得了,但结合当时的考情回忆和后来多方交流,大致分成四块:选择填空题、简答题、手写代码题、开放问答。
第一块是基础概念题,主要覆盖HTML、CSS和JavaScript基础,比如盒模型、事件冒泡与捕获、原型链、this指向等。这类题考的是基础功底扎不扎实,几乎不涉及复杂场景,但会在细节上挖坑。
第二块是简答题,通常是让你简述某个工作原理或者分析一段代码的运行结果,比如事件循环的执行输出顺序、isNaN和Number.isNaN的区别这类。
第三块是手写代码题,这块是重头戏,包括数组去重、防抖节流、深拷贝、排序算法、函数柯里化等。笔试时间很紧的话,这块最容易拉开差距。
第四块是开放题,像“如何评价一个前端框架”“如何设计一个组件”这类,考察你的思路是否结构化,以及对领域内的思考是否到位。
我建议的做题顺序是:先写简单题和选择题,再集中时间攻手写代码题,最后留至少三十分钟给开放题。很多人习惯从第一道题做到最后一道,结果卡在某个闭包题上半小时,后面的代码题基本来不及。笔试的容错率其实比想象高,但最怕的是时间利用不合理。
2. 重头戏一:JavaScript基础与手写代码题
2.1 高频考点:原型链、作用域与this指向
先说原型链。铅笔题里很喜欢出类似“定义一个构造函数,给它的原型上挂方法,然后new一个对象,调用这个对象上的方法,问输出结果是什么”的题目。这道题的主体很简单,但坑往往在细节上。比如方法里用了this,而调用方式改变了this的指向,结果就可能完全不一样。
我当时拿到这类题,第一件事不是直接写,而是在草稿纸上把原型链的查找过程画出来。实例对象找属性,先看自身,再看构造函数prototype上的属性,再看原型链上层。当题目中出现a.__proto__.method和a.method的区别时,本质是在考“方法调用时this指向调用者”这个规则。
再说作用域和闭包。经典的“for循环里用var定义i,循环里绑定点击事件,点击后输出i”的题目,不管在哪家企业笔试里都出场率极高。这道题答案不唯一,可以讲用let、可以讲用闭包、也可以讲用事件对象,但更重要的是你得说清楚背后的原理:var没有块级作用域,循环结束后i已经变成最终值,事件回调执行时读取的是同一份变量。
this指向这块,出题思路一般是让你判断一段代码的执行结果。常见的坑包括:普通函数调用、对象方法调用、构造函数调用、箭头函数定义时的上下文捕获。记住一个朴素原则:大部分情况下this指向“调用时”的对象,箭头函数则看“定义时”的环境。考题里还会混着call、apply、bind,你要能把三者的联系区分开并手写一个bind的实现,这在手写题里也是高频题。
2.2 手写题的基本答题策略
手写代码题是笔试卷子里最硬核的部分,因为机械性地背诵代码模板没有用,面试官看的是你怎么思考、怎么组织逻辑、有没有处理边界条件。
我总结了一个三步答题法,后来带新人时也一直用:
第一步,先把函数签名写出来,明确输入和输出。比如要你实现数组去重,就要先界定“输入一个数组,输出一个没有重复元素的新数组”,原始数组是否允许改变,需要你自行说明并处理。
第二步,写主逻辑时先保证正确性,再谈优化。很多同学一上来就写Array.from(new Set(arr)),但完全没有考虑数组里有NaN、对象等复杂类型时Set的行为。我在批改时更看重的是:你是否想到了多种解法之间的差异,比如双重循环、indexOf、includes、Set,以及各自的时间复杂度和适用场景。
第三步,边界条件和特殊值处理。空数组、null、undefined、对象元素、负数、极大数,这些在笔试中可能不会专门给测试用例,但你主动考虑到,完全可以在答案里加注释说明,会让面试官觉得你平时写代码就有关注异常情况的习惯。
2.3 防抖节流、深拷贝与函数柯里化的实战记忆法
这三个手写题几乎在每种前端笔试里都会出现,蘑菇街的题也不例外。
防抖和节流,市面上有很多版本的实现,但核心要义就两条。防抖是在事件停止触发后delay毫秒再执行,适合搜索输入这种场景;节流是每隔delay毫秒最多执行一次,适合滚动监听和resize这种高频事件。我在做题时不会直接背代码,而是先想应用场景,再推导代码。比如防抖,你理解为“回城被打断,重新读条”,代码思路就出来了:每次调用先清掉上一次的定时器,再重新设一个新的定时器。节流则理解为“技能CD”,CD期间不管你怎么按都不触发。
深拷贝这个题坑最多,也最容易写一半卡住。最容易被忽略的是循环引用,比如对象a的某个属性指向a自己,如果没有处理就会无限递归。其次要注意Date、RegExp、Map、Set这些特殊对象的拷贝方式。还有Symbol作为属性名的情况也不能漏掉。我当时的处理方法是,使用WeakMap记录已经拷贝过的对象,遇到循环引用时直接返回缓存里的副本。
函数柯里化在笔试题里出现的形式一般有两种:一种是你知道概念,实现一个curry函数把多参数函数变成链式调用;另一种是直接让你实现add(1)(2)(3)这样的累加器。这里的核心是用闭包缓存参数,参数个数不够就返回新函数,攒够了就执行原函数。写的时候要注意设计一个合理的机制来判断参数是否收齐,我习惯用fn.length来拿原函数的形参个数。
3. 框架与工程化:Vue相关的出题方向
3.1 Vue响应式原理与NextTick的考点
蘑菇街前端业务早期版本大量使用Vue,所以笔试中Vue的占比相当高。其中响应式原理是必考中的必考。
Vue 2的响应式原理是基于Object.defineProperty实现的,Vue 3则改为基于Proxy。面试题不会只问你“知道这两个API的区别”,而是会深入到“为什么Vue 2无法检测到数组下标的变化”“为什么新增对象属性不是响应式的”“这些坑在业务中怎么绕过”。你要是只背一句“Vue 3用Proxy解决了这个问题”,显然是不够分的。
我建议你在复习时亲手搭一个小DEMO,用Vue 2的源码里那段Observer、Dep、Watcher的逻辑走一遍流程,理解数据变化如何触发视图更新。这个理解不光是为了过笔试,后面做复杂组件排错时真的能救命。
NextTick也是一个高频考点。题目通常不会直接问“NextTick是什么”,而是给出一个场景:你在修改数据之后立刻访问DOM,发现拿到的还是旧值,问为什么。这背后是Vue更新DOM的异步策略,源码里用微任务队列来调度更新。你答到“DOM更新是异步的,需要在下一次tick里读取”是基本分,如果能讲清楚为什么用微任务而不是宏任务,以及它和前端的EventLoop关系,就能拿到加分项。
3.2 组件通信与抽象设计考核
组件通信是业务前端绕不开的能力。笔试里会出现诸如“父子组件如何通信”“兄弟组件如何通信”“深层嵌套的组件又想触发一个事件怎么办”这类问题。在我印象中,蘑菇街的题目会更偏实践,比如给你一个商品卡片组件,问你它的props该怎么设计、事件该怎么往外派发。
答题时要尽量有层次感。先说父传子的props、子传父的emit,再说event bus或者Vuex/Pinia这类全局状态方案。如果是在Vue 3里,还可以提provide/inject。最后一定要落到场景适配,比如“简单的通知类操作用emit就够了,跨多层级共享状态用store来管理更清晰”。这样显得你有判断力,而不是罗列API。
组件抽象设计这块,笔试题可能会让你设计一个弹窗组件、一个页码组件或者一个无限滚动列表组件。考察的核心点不是组件怎么搭,而是你能否把“通用能力”和“业务逻辑”分层。好的组件设计应当对外暴露清晰的props和events,内部实现细节尽量封死。比如设计一个弹窗组件,我会先列props:visible、title、width、closeOnClickOverlay,再列events:update:visible、closed,最后用插槽放入自定义内容。这样写出来,即使不写完整代码,也能看出你有组件化思维。
3.3 构建工具与前端规范知识
笔试里不一定要求你手写webpack配置,但很可能会给你一段配置文件,问你“这段配置干了什么”或者“这里为什么要加这个loader”。那些年主流的构建工具还是webpack,你需要知道entry、output、loader、plugin这几个核心概念,以及常见的loader比如babel-loader、css-loader、style-loader各自解决什么问题。
另外一个常见考点是模块化的演进:从全局变量到CommonJS、AMD、ESModule。至少要清楚require和import的区别,以及ESModule是静态编译、CommonJS是运行时加载这两者的差异。
还有一类题目是考“代码规范”的,像“你在团队里怎么推行ESLint和Prettier”“如果让你定一套前端代码规范,包含哪些内容”。这类题没有标准答案,但特别能看出你有没有真实协作经验。我当时答的是从命名规范、组件规范、目录结构规范、提交信息规范四个维度去展开,并举了自己团队里实际踩过的因代码风格不一致造成的合并冲突问题。这种回答一听就不是背出来的。
4. CSS布局与移动端适配:电商前端的必考题
4.1 经典布局题的解法对比
CSS布局题在蘑菇街的笔试题里不会缺席。比较典型的是让你实现“左侧固定宽度200px,右侧自适应”的双栏布局,以及“三栏布局,中间自适应,两边固定”的圣杯布局和双飞翼布局。
这些题目的本质是考察你对几种布局方案的掌握程度,以及你在不同场景下的取舍能力。浮动布局的写法网上到处都是,但你要能说清楚它的缺点,比如需要清除浮动、父容器高度塌陷。绝对定位的写法虽然简单,但依赖父容器有定位属性,且对文档流的破坏性比较强。flex实现双栏布局是最实用的,一行代码就能搞定右侧自适应。
近两年CSS Grid的普及率越来越高,如果出到“九宫格布局”“两行三列自适应”这类题,用Grid几乎是降维打击。我的建议是布局方案不要只背一种,而是按场景分类记忆:常规页面布局用flex,二维网格类用Grid,传统兼容场景再用float。笔试作答时可以把两种实现都写出来,对比一下优劣,面试官会觉得你有工程判断力。
还有一道很经典的题是“如何居中一个元素”,水平居中、垂直居中、水平垂直居中要各写几种方式。这个题看似简单,但你可以写出七八种方案,而且每种的适用前提都不一样。我后来在面试新人时特别喜欢在这道题上加问一句“如果这个元素不知道宽高呢”,能答上来用transform或flex的人,说明是理解而不是背答案。
4.2 移动端适配:rem、vw与viewport的取舍
移动端适配是电商前端笔试的重点,常考一道“你如何做移动端适配”的简答或方案设计。这个考点主要看你的方案是否能在多机型上保持一致的呈现效果。
最早的解决方案是固定宽度加viewport缩放,但这种方式在iPhone X这种长屏机型上会出现明显问题。后来用rem方案,以根元素字体大小为基准动态计算,配合flexible.js做屏幕宽度适配。现在比较推荐的是直接用vw单位,UI稿标注750px宽度时,直接写calc(100vw * 50 / 375)这样换算出需要的vw值。不过vw在处理字体和大尺寸圆角时精度不如rem直观,有些团队仍会混合使用。
笔试题里如果出“设计稿是750px宽,一个按钮宽300px,请给出rem或vw的实现”,其实就是在考你能不能把标注稿的单位换算逻辑写明白。我当时答的是以375px为基准屏,根元素font-size设置为100px,按钮宽度则写成300 / 100 = 3rem。这个换算虽然机械,但能看出你对适配方案的理解是完整的。
4.3 渲染机制与性能优化基础
前端页面性能优化的题目在电商类公司笔试里几乎是必出的,因为页面的加载速度和交互流畅度直接关系交易转化率。笔试一般不会让你做性能分析,但会考关键渲染路径、重排重绘、图片懒加载这些基础概念。
关键渲染路径的题,我通常这样答:先解析HTML成DOM树,解析CSS成CSSOM树,两者合并成渲染树,然后进行布局计算和绘制。JavaScript的执行会阻塞这个过程,所以脚本应该放在body底部,或者使用defer和async属性来避免阻塞。如果题目问“如何优化首屏加载速度”,就要把删除阻塞渲染的资源、压缩JS/CSS体积、使用骨架屏、图片懒加载、拆包按需加载这些手段都列上去。
图片懒加载也是电商业务里非常实际的问题。商品列表页动辄几十张图片,不加懒加载的话,首屏请求数会爆炸。实现上,你可以用IntersectionObserver来监听图片是否进入视口,进入后再把src替换为真实地址。这个方案比传统的scroll监听加getBoundingClientRect写法性能好很多,而且代码量更少。手写代码题如果让你实现懒加载,我建议优先写IntersectionObserver版,同时补一个scroll兼容方案,以示你了解兼容性处理。
5. 算法前置知识:不考LeetCode硬核题,但考基础
5.1 数组、字符串与排序去重的组合题
蘑菇街的笔试算法题没有到LeetCode中难题的程度,但也不是完全送分。高频出现的组合是数组去重、数组扁平化、冒泡和快排、二分查找、以及处理字符串类的判空、反转、重复字符统计。
数组去重这个题前面提过了,真正的加分点在于你答出不同方法的适用场景。比如小数组用Set简洁高效,大数组且需要保留顺序时可以考虑Map或对象降维,包含复杂类型元素时就得自定义比较规则。
数组扁平化也是一个常见题,考察递归和reduce的掌握程度。[1, [2, [3, [4]]]]要变成[1, 2, 3, 4],可以用flat函数的参数控制展开层数,也可以手写递归。我在笔试里遇到这个题时,还专门写了处理循环引用的版本,虽然可能不是出题人期待的标准答案,但至少展示了边界意识。
排序算法不需要你写出每种排序的工程级优化,但至少要能手写冒泡和快排,并且知道两者最坏时间复杂度。我建议把快排背熟一点,因为它可以同时考察到递归、分治、数组操作三个知识点。如果你的笔试题里有一道“实现一个快速排序”,答完后顺手加一句说明快排在工程中为什么通常优于冒泡,会让面试官觉得你理解的不只是代码层面。
5.2 递归、栈与二叉树的送分题
有一些简单的数据结构题,很多人会觉得算法题里不会考,但实际上二叉树递归遍历这类题是经常出现的。原因很简单:在电商前端的业务代码里,处理树形结构(如分类菜单、地区联动、组织架构)是高频场景。笔试里让你“实现一个二叉树的先序遍历”,看起来是很教科书的东西,其实是借用二叉树这个壳,考察你递归能力。
递归题型的答题要点就是先写终止条件,再写递归过程调用。我踩过最重要的坑就是递归函数里忘记更新参数,导致无限递归。笔试时间紧张,写完代码要用测试数据在脑子里跑一遍流程,别交卷了才发现栈溢出。
栈这个数据结构在笔试里也有出场,常见的有“判断括号字符串是否合法”“模拟浏览器前进后退”等场景。前者是经典算法题,但实现起来不复杂,遇到左括号入栈,遇到右括号出栈并匹配,最后栈为空则合法。后者其实和浏览器History API的实现思路类似,用两个栈维护“后退栈”和“前进栈”的状态。
6. 开放题与HR面:怎么写出有层次感的思考
6.1 框架评价与工具选型的回答思路
开放题通常是整张卷子里最灵活的部分,比如让你“谈谈对Vue和React的看法”,“如何选择团队的技术栈”,“对一个页面的性能优化方案进行设计”。这些题没有固定答案,但最忌讳答得空洞。
我在带新人时经常说,这类题不要试图求大而全,而是要展示一个完整的思考链路。比如谈Vue和React,先说自己熟悉的核心特性,再从数据流、响应式机制、模板与JSX几个维度去对比,最后落到“选型时取决于团队熟悉度和项目场景”这个开放结论上。这样即使你的观点并不新颖,结构化的表达也会比零散的个人感受强。
工具选型题也一样。答法套路是:分析需求场景,列出候选方案,比较关键指标,给出风险预案。比如你选状态管理方案,可以从生态成熟度、类型支持、学习成本、团队掌握度四个维度来对比Vuex、Pinia、Redux Toolkit,最后给出推荐并按业务复杂度分级。
6.2 笔试题中容易忽略的非技术坑
有几类非技术细节,考前没注意的话很容易被悄悄扣分。
第一是卷面排版和代码格式。手写代码题如果代码缩进混乱、函数命名随意、变量名杂乱无章,面试官会默认你平时代码风格也不好。答题时保持基础的代码可读性,比多写一个炫技方案更加分。
第二是审题不清。比如题目要求“不能修改原数组”,结果你直接在原数组上sort了,这种失误非常可惜。拿卷子时先花两分钟把每道题的要求读一遍,尤其是“返回值类型”“是否改变原数据”“是否需要处理边界条件”这几个点。
第三是时间分配。前面说过,不要在选择题和简答题上花比例过高的时间。我当时就是在一道this指向的简答题上钻了太久,导致最后一道设计组件题只写了一部分。后来想通了,笔试是选拔,不是证明自己每个点都会,拿到能拿的分最重要。
7. 踩过的坑与复盘建议
7.1 考场上我吃过亏的三种情况
现在回头看那份卷子,我踩过最大的坑是手写代码题没检查边界条件。有一道数组处理题,我主流程写得很快,但漏掉了对空数组的判断。笔试环境下没有编译器也没有测试用例,代码是直接写上去的,这种小遗漏只能靠平时多练习,形成条件反射。
第二个坑是Vue响应式题目上,我把Vue 3的Proxy特性和Vue 2的Object.defineProperty原理掺在一起答了。面试官可能知道你想展示知识面,但答题时混淆版本会给人概念不清晰的印象。建议写答案时先明确声明“我以Vue 2为主来分析”,如果要提Vue3的新方案,单独作为对比补充。
第三个坑是开放题写太少。一开始我觉得开放题没有标准答案,简单写几句就跳到下一题了。后来发现这类题其实是难得的展示思考深度的机会,如果你只写两行,等于把加分机会白白扔了。哪怕不会,也要把分析框架列出来,写出可能的思路方向,尽量不要留白。
7.2 考后复盘:从错题里提炼知识点
笔试结束不代表学习结束。我建议无论通过与否,都要把整张卷子复盘一遍。具体做法是:把每道错题或犹豫过的题目整理成一张表,列三列,第一列是题目考察的知识点,第二列是自己当时的错误答案或卡壳点,第三列是正确思路或改进后的写法。这样一张表基本就是你的求职冲刺资料。
我当时就是靠这个表格,把原型链、事件循环、内存泄漏、移动端适配这几个薄弱点挨个补上的。刷题量不在多,把一套硬题吃透,比做十套水题更有用。
还有一个小技巧:把所有手写题的代码重新在本地跑一遍,测试各种边界数据。笔试时不敢写或者写不完整的代码,在IDE里跑通了,才能真正变成你的能力。就像我当时写防抖函数时还在困惑为什么this要用context保存,直到自己跑了几个用例才彻底明白。
最后一个提醒
这份笔试题再经典,也只是校招前端求职路上的一个节点。真正拉开差距的,不是某道题的答案,而是你平时写代码时有没有把每个API背后的机制弄明白。我见过很多同学把题库刷了十几遍,但一遇到变体题就懵,原因就是只记答案不记思路。所以我的建议很朴素:每做一套题,别急着对答案,先把自己的思路写出来,再对照标准答案找差距。这个过程虽然慢,但长期看是最快的学习路径。
如果你正在准备类似方向的前端笔试,希望这篇拆解能帮你看清题背后的考察逻辑,顺利拿下心仪的offer。