资讯动态

途虎养车前端笔试题复盘:从JS核心到业务设计全覆盖

发布时间:2026/8/29 23:31:51 来源:尧图企业网站定制
途虎养车2023秋招前端笔试试卷B这份卷子我到现在印象还挺深。当时做完最大的感受是题量不小覆盖面广而且很多题不是单纯背八股文就能答好的尤其是后半段的业务场景和设计题明显能感觉到公司在筛选“能干活、能落地”的前端而不是只会背概念的人。途虎的业务形态比较特殊线上是电商和内容社区线下有几千家门店所以前端要面对的场景非常杂从H5活动页到订单履约、从门店地图到售后工单这套试卷基本就是围绕这些真实业务逻辑展开的。对于准备秋招或者暑期实习的同学来说这份卷子值得认真复盘一遍。作为亲身做过这套题的人这篇内容我就按试卷的实际结构和考察逻辑来拆解每一类题型我都会还原考点、给出答题思路再补充一些考场上容易踩的坑。准备前端面试的同学可以把这篇当成一份“带答案的复盘笔记”来看比单纯刷题库更有参考价值。1. 整张试卷的定位与命题风格先聊整体印象。B卷整场考试时间是90分钟满分100分题型分布大概是单选题10道20分、填空题5道10分、简答题3道15分、手写代码题3道25分、综合设计题2道30分。这个分值分布本身就说明了问题选择和填空加起来只占30%剩下70%全在考察你能不能写出东西、设计方案。B卷和A卷的差异我当时和同批笔试的同学对过题一个很明显的区别是A卷偏基础选择题占比高一些手写题相对简单B卷明显更吃业务理解综合设计题给的都是实际业务场景要求你输出完整的实施方案。我猜这可能和面试官分的批次有关也可能B卷本身就是给前端岗位技术面之后加试用的但不管怎样B卷想筛出来的一定是“有工程经验、能思考方案”的人。1.1 考点分布和权重我按记忆把考点大致归类了一下占比从高到低排序大概是下面这个结构。考察方向分值占比代表题型JavaScript 核心机制25%this指向、事件循环、闭包、原型链手写框架应用Vue为主20%响应式原理、组件通信、生命周期场景工程化与性能优化15%Webpack/Vite、首屏优化方案网络与浏览器10%缓存机制、跨域方案、HTTP状态码算法与手写题20%Promise.all、防抖节流、版本号排序业务方案设计10%错误监控SDK、门店选择器组件设计这个权重分配其实挺有参考意义的。框架只占20%算法手写占20%而纯JS基础占到了25%——说明途虎这种业务量级的公司比“你会用什么框架”更看重“你懂不懂语言本身”。框架可以学JS底子不扎实的人在复杂的业务场景里很容易写出问题这个逻辑放到现在依然成立。1.2 为什么题目长这样业务驱动命题途虎在汽车后市场里属于头部玩家前端团队要维护的项目包括PC官网、移动端H5商城、小程序、门店SaaS系统、内部中后台以及大量营销活动页。这种业务结构决定了他们必须招“什么都能干”的前端所以试卷里才会既有低层API的实现考察又有高层方案设计。理解了这一点你会发现试卷里每一道题都不是随便出的。比如它考你Vue的响应式原理是因为门店系统中大量数据是实时变化的库存在变、订单状态在变、技师排班在变响应式一旦写错整个页面数据就全乱了。它考你HTTP缓存是因为移动端H5在全国门店的网络环境下差异很大2G、3G网络还有用户在用缓存策略直接影响页面加载速度。所以后面我复盘题目的时候不建议只背答案最好连“为什么考这个”一起想明白。2. JavaScript 核心机制选择、填空和简答的重灾区这部分是整张卷子的地基。选择题和填空题里大概有七八道都是围绕JS的语言特性出的简答题里也有一道直接和事件循环相关。说实话这些题本身难度不算高但坑非常多稍不注意就会被细节绕进去。2.1 原型链与继承的经典考法印象最深的一道选择题是关于原型链的大致是这样的function Person() {} Person.prototype.say function() { console.log(person) } const p1 new Person() const p2 new Person()问p1.say p2.say的结果以及p1.__proto__ Person.prototype是否为 true。这个题看似基础但里面藏着几个点new的时候发生了什么——创建对象、绑定原型、改变 this、返回对象。实例方法是通过原型链查找的不是每个实例单独复制一份。修改Person.prototype.say会影响所有实例因为方法不在实例上而在原型上。还有一个高频变体是让手写寄生组合式继承这个属于“背下来就能写不理解就会卡壳”的题。核心思路是创建一个空函数中间件让子类原型的__proto__指向父类原型避免执行父类构造函数。我当时直接写了一个Object.create版本两三行搞定function inherit(child, parent) { child.prototype Object.create(parent.prototype) child.prototype.constructor child }注意一定要把constructor指回来不然child.prototype.constructor会指向parent这在多人协同时排查起来很恶心。2.2 事件循环别再只看宏任务微任务的顺序文章简答题给了一段代码让写出控制台输出顺序。大概是这样的变体console.log(1) setTimeout(() { console.log(2) Promise.resolve().then(() console.log(3)) }, 0) new Promise((resolve) { console.log(4) resolve() }).then(() console.log(5)) console.log(6)我估计很多同学第一反应是背“先同步后异步、微任务先于宏任务”然后写 1、4、6、5、2、3。这个顺序是对的但面试官如果想深挖会追问2 和 5 谁先3 在哪里输出也就是要你区分微任务队列和宏任务队列的执行时机。这里我的建议是不要停留在背结论你要能在脑子里模拟出事件循环的完整过程。通俗理解就是把事件循环当成一个永远在排队的食堂窗口同步代码是已经占了座的人宏任务是正在排队的人微任务是每一个宏任务处理完之后突然插队进来的人。宏任务每消费一个就要把当前所有微任务清空才会去拿下一个宏任务。考场上还有一个容易忽视的细节setTimeout的定时器即使写 0也有至少 4ms 的延迟而Promise.resolve().then在同一轮里会先在微任务队列里执行完。所以如果题目里既有setTimeout(fn, 0)又有多个PromisePromise回调永远会在setTimeout回调之前执行除非前一轮的微任务还没清空。2.3 this 指向和闭包笔试最爱的组合拳选择题里有一道关于对象方法里 this 的题const obj { name: 途虎, getName: function() { return this.name } } const fn obj.getName fn()答案是undefined因为fn()是普通函数调用this 指向全局严格模式下是 undefined和obj.getName()的调用方式完全不同。这个题太经典了但每次考每次有人错因为它同时考察了“谁调用、this 就是谁”和“函数被取出后上下文不复存在”这两个机制。闭包那道填空题也很有代表性给定一个for循环 var声明 setTimeout的组合要求写出最终输出。这是老八股文了解法是改成let或套一层立即执行函数。但笔试的坑在于如果改成let输出是 0 到 n-1这个大家知道如果要求你“用闭包解决”你得写出for (var i 0; i 5; i) { (function(j) { setTimeout(() console.log(j), j * 100) })(i) }这个题说明一件事途虎的笔试非常吃“能不能用最原始的手段解决问题”因为实际业务里你不可能永远用框架层 API总会有需要自己控制作用域的时候。3. Vue 框架与移动端业务场景题框架题分值不算最高但非常贴近实际业务。卷子里的约三题是 Vue 的而且都集中在“原理场景”的交叉点上没有让你死背 API。3.1 响应式原理从 Vue2 到 Vue3 的演进简答题有一道是让对比 Vue2 和 Vue3 响应式的实现方式并说明为什么 Vue3 要改。我在答的时候分了几层说Vue2 用的是Object.defineProperty核心是递归遍历对象的所有属性做劫持。问题在于属性新增、删除检测不到所以要额外提供Vue.set/Vue.delete。数组的索引变化检测不到所以必须重写数组的七个方法。对象层级很深时递归初始化性能很差。Vue3 用的Proxy直接代理整个对象所以新增删除都能拦截数组也不需要单独 hack性能上惰性代理也更好。但它也有缺点比如兼容性要求更高Proxy的拦截粒度导致一些静态分析工具难做。这套题我自己总结了一个回答模板先说原因业务复杂度增加再说原理各自实现方式最后说代价兼容/重写成本。这种结构不管是笔试还是面试都很好用因为面试官能从你的分层里看到你“既知道优点也知道缺点”这是工程思维的体现。3.2 key 的作用和 diff 算法填空题里有一道是v-for 渲染列表时为什么要指定 key并且 key 建议用什么。这个题表面的答案是“帮助 diff 算法识别节点减少不必要的 DOM 操作”但笔试的坑在于它会继续往下问如果不写 key 会怎样如果 key 用 index 会有什么隐患不写 key 时Vue 会采用就地复用策略in-place patch在列表中间插入新项时可能会导致 DOM 状态错乱比如带 input 的列表项用户输入的内容会被错误地复用到别的行。key 用 index 也有同样的问题因为 index 在插入和删除时都会变化导致 diff 认为每个节点都变了。所以正确的 key 应该是业务唯一 ID比如订单号、商品 ID、门店 ID。这个点是途虎这种业务里非常核心的痛点——门店列表、订单列表、工单列表全是带状态的列表项key 写不对线上就会出现“明明数据是对的但页面渲染错乱”的诡异问题排查起来非常痛苦。3.3 业务场景题门店选择器怎么设计综合设计题里有一道让我印象特别深它给了一个真实场景门店选择页需要从全国几千家门店中筛选出“距离最近、可提供服务、当前营业中”的门店列表在移动端 H5 上展示要求设计组件的实现方案。我当时是从这几个方向展开的数据层面不能用前端全量拉取几千家门店做本地过滤应该是后端基于经纬度分页返回前端最多做到滚动加载。距离计算放在服务端前端只传经纬度和筛选条件。状态层面组件的状态包含当前位置、筛选条件、列表数据、加载状态、空状态、错误状态。这里要重点说清加载更多和下拉刷新的状态互斥问题避免重复请求。交互层面定位权限的引导、定位失败的降级方案手动输入城市、筛选条件的联动服务项目改变后可用门店数要跟着变。性能层面列表用虚拟滚动定位成功后不要重新创建整个组件而是通过响应式数据更新触发局部刷新避免整个页面白屏。这种题没有标准答案但它能非常直接地体现你的工程思维。写的时候尽量把每个方案落地到“用什么技术、为什么用它、边界是什么”而不是给一个泛泛的方案。4. 工程化与性能优化笔试拉开差距的地方这部分在试卷里占的分值不算最多但简答题和设计题都涉及属于“会的人能拿满分不会的人只能写两三行”的模块。而且说实话我在秋招前刷了很多题库很多人都把精力放在 JS 和框架上忽略了工程化结果遇到途虎这套卷子就有点懵。4.1 Webpack 构建流程最容易被问的细节有一道简答题问的是 Webpack 的构建流程以及 Loader 和 Plugin 的区别。这个题很常见但比较容易答得笼统。我当时的答法是把流程拆成了四条主线初始化参数从配置文件读取参数合并命令行参数。编译从入口出发解析模块依赖通过 Loader 转换非 JS 模块。构建模块生成模块的依赖图和 chunk。输出根据配置生成最终文件Plugin 在整个过程中通过钩子参与。Loader 和 Plugin 的区别一句话说清Loader 是“让 Webpack 能认识新文件类型”本质是文件转换器Plugin 是“扩展 Webpack 本身的能力”通过监听生命周期钩子来做更复杂的事。再直白一点Loader 像一个翻译官把 Webpack 听不懂的语言翻译成它懂的语言Plugin 像一个工程经理能在项目构建的各个阶段插入和干预。然后我还写了一个自己用过的场景写一个简单的 Plugin在构建完成后把文件大小输出到控制台。这个属于加分项虽然不一定要求你写完整代码但能让面试官看到你真的用过。4.2 首屏优化方案最好结合业务说设计题里有一道是一个面向车主的 H5 活动页面首屏需要展示活动主视觉、用户当前城市、附近门店列表要求在弱网环境下快速打开你会怎么做。我当时答的优化方向是骨架屏加首屏直出先渲染页面框架数据通过接口异步填充避免白屏等待。图片压缩和 CDN 加速主视觉图用 WebP 格式按屏幕尺寸裁剪多套尺寸用srcset让客户端选合适的。懒加载与预加载的组合首屏只加载关键资源非首屏图片懒加载同时用link relpreload预加载门店列表接口的 DNS 和连接。接口合并首屏需要的城市信息和门店列表合并成一个接口返回减少 RTT。缓存策略活动页静态资源带上 hash 用强缓存同时设置协商缓存做兜底。写完这些我觉得还不够就补了一句在做优化之前需要先设定衡量指标比如 FCP 从 2.8s 降到 1.5sSSR 或 TTI 变化多少。这个补充其实很重要因为业务方关心的是结果你只给一堆优化手段不如给出“做完之后性能提升多少”的预期。4.3 浏览器缓存怎么答才算完整选择题里有一道关于缓存的题问强缓存和协商缓存分别由哪些响应头控制、优先级是什么。这个题不难但我建议答的时候把整个过程串起来第一次请求服务器返回资源和Cache-Control/Expires。第二次请求浏览器判断缓存是否过期未过期直接用强缓存不发请求。过期后带上If-None-Match/If-Modified-Since发请求服务器比对资源返回 304 或者新资源。优先级上Cache-Control高于ExpiresETag优先于Last-Modified因为 ETag 更精确能处理秒级以内的修改。笔试里容易丢分的是到底哪些资源适合强缓存、哪些适合协商缓存。我的经验是带 hash 的静态资源JS/CSS/图片用Cache-Control: max-age31536000, immutable因为文件名变了就是新资源HTML 文件用no-cache保证每次回源验证因为 HTML 往往是页面入口引用的静态资源 hash 变了HTML 必须跟着更新否则会引用到老版本。5. 手写代码题和算法题考的不只是你会不会写手写题是这套卷子拉开差距的关键。题量虽然只有三道但每一道都要求“能跑”的程度而不是“大概写写思路”。我记得当时时间最紧张的就是这部分因为前面简答题写得有点细到手写题的时候只剩半小时了。5.1 手写 Promise.all注意边界才能满分手写Promise.all是前端笔试的常客它的考察点很集中接受一个可迭代对象返回一个新的 Promise全部成功时结果按传入顺序返回有一个失败整个 Promise 失败。我在想的时候重点处理了两个边界一是参数不是 Promise 时要用Promise.resolve包装二是空数组的情况直接 resolve 空数组。代码大概是function myPromiseAll(promises) { return new Promise((resolve, reject) { const result [] let count 0 if (promises.length 0) { resolve(result) return } for (let i 0; i promises.length; i) { Promise.resolve(promises[i]).then((data) { result[i] data count if (count promises.length) { resolve(result) } }).catch(reject) } }) }这里有个考点是为什么用count计数而不是直接判读result.length因为数组result[i] data的方式在全部完成之前可能有些位置是空槽length已经等于 promises.length 了但实际还没全部执行完。所以必须用额外的计数变量。5.2 手写防抖和节流光写出来不够防抖和节流那道题要求写出实现和适用场景的对比。代码很多同学都会关键是后面的场景题。防抖适用的场景是搜索框输入联想、窗口 resize、按钮点击提交核心是“最后一次操作后等待一段时间再执行”。节流适用的场景是滚动加载、拖拽、游戏中的帧率控制核心是“保证固定时间内只执行一次”。我在代码里特意实现了带 cancel 的版本因为后续面试官很可能会追问“如果用户快速输入完又立刻离开页面定时器还在怎么办”这时候用 cancel 清掉定时器就能避免组件卸载后还触发 setState 带来的内存泄漏。5.3 算法题版本号排序的套路最后一道算法题是给了一组版本号数组比如[1.0.1, 1.0.0, 2.1.0, 1.1.0, 2.0.10]要求排序成从新到旧。这个题经典在它考的是“字符串比较”和“数值比较”的区别。如果用默认的字符串排序2.0.10会被排到2.0.2前面因为字符串比较是逐位比较的。正确做法是先把版本号按.分割成数组然后把每一段转成数字再比较。我写了大概下面这样的思路const versions [1.0.1, 1.0.0, 2.1.0, 1.1.0, 2.0.10] versions.sort((a, b) { const pa a.split(.).map(Number) const pb b.split(.).map(Number) const len Math.max(pa.length, pb.length) for (let i 0; i len; i) { const x pa[i] || 0 const y pb[i] || 0 if (x ! y) return y - x } return 0 })这个解法能覆盖版本号位数不同的情况比如2.0和2.0.1比较到第二段时pa[2]是 undefined用|| 0兜底。这种边界处理是笔试加分项也是实际业务里最容易出 bug 的地方——版本兼容逻辑写不好线上 App 升级就全乱套了。6. 综合设计题拉分最大也最考验积累综合设计题虽然只有两道但每道题的分值都在 10 到 15 分如果只是潦草写几点很难拿高分。我反而觉得这是整张卷子最有意思的部分因为它更接近真实工作的状态——给你一个模糊需求要你自己定方案。6.1 前端错误监控 SDK 设计有一道题问的是如果让你从零设计一个前端错误监控的 SDK你会采集哪些内容、如何处理上报、如何保证不影响业务性能。我在答的时候是分四层来写的第一层采集层。全局监听window.onerror捕获 JS 运行时错误unhandledrejection捕获 Promise 异常window.addEventListener(error, ..., true)捕获资源加载失败另外还要主动上报接口请求失败配合 axios 拦截器。采集的信息除了错误堆栈还要带上发生时的页面 URL、用户操作路径用埋点记录的前几步操作、设备信息UA、屏幕尺寸、网络信息Network Information API。第二层处理层。错误去重同一错误在短时间内只上报一次防止刷屏堆栈信息做脱敏去掉项目本地路径和用户隐私数据对于跨域脚本错误如果 CDN 资源设置了crossorigin才有办法拿到完整堆栈否则只能获得 script error。第三层上报层。优先用sendBeacon发送因为页面卸载时 XHR 可能被浏览器中断如果 sendBeacon 不可用降级为创建一个 1x1 像素的 Image 对象上报GET 请求天然不受跨域限制。同时要做一个缓存队列上报失败就存到 localStorage等网络恢复再补发。第四层消费层。把 sourcemap 上传到单独的服务错误堆栈还原出原始代码位置。这里要注意sourcemap 不能公开部署在用户能访问的地方否则等于把源码送到别人手里。这套框架我当时写完之后还留了几行备注说“为什么不用 window.onerror 之外的方式作为主采集”因为window.onerror覆盖面最广、兼容性最好vue.config.errorHandler这类框架层面的捕获可以作为补充而不是替代。这种“补充思路”在笔试里非常加分它让阅卷人觉得你不是在背模板而是真的想过。6.2 大型活动页的工程化方案另一道设计题是针对一个短期上线的品牌活动页面说你会怎么搭建这个项目包括构建工具、目录结构、状态管理、上线方案。我的整体思路是活动页的生命周期很短但并发量可能很高所以项目应该按“可快速开发、可稳定部署、可随时回滚”的目标来搭。构建工具选 Vite因为活动页不需要太复杂的配置开发热更新快而且活动迭代频繁快速启动很重要。目录结构按业务模块划分而不是按文件类型划分components 里放活动页专属组件utils 放独立工具函数api 按页面拆分接口。状态管理视复杂度而定如果只是几个页面间的共享状态用 Vuex/Pinia 的 module 或 React 的 Context 就够了没必要为一个小活动页引入重型状态管理库反而增加心智负担。上线方案采用多环境部署灰度发布活动页配置了开关出现问题可以立即下掉资源而不是改代码重新发布。性能保障活动页静态资源放 CDN图片用瘦身压缩和 WebP 格式首屏交互提前做骨架屏活动用券的接口要防刷。这道题其实没有唯一答案但它能看出你理解“工程化”到什么程度。真正做业务的人知道活动页最重要的是“快”和“稳”不是“技术栈多先进”。如果只是堆一堆时髦名词反而会给阅卷人留下不靠谱的印象。7. 笔试复盘考完之后我总结的避坑清单卷子交上去之后我大概过了两三天才缓过劲来认真复盘了一遍自己哪里做得不好、哪里答得太啰嗦、哪里完全没想到。分享几个我总结的心得后面准备笔试的同学可以直接参考。7.1 丢分点统计我把自己当反面教材丢分类型具体表现加深原因概念不精准Vue2 的数组响应式缺陷答成“不能检测”实际是“不能通过索引赋值检测到”背结论没背到底层逻辑边界缺失Promise.all 的空数组情况直接没写只关注主流程忘了临界条件手写不稳防抖的 cancel 版本没实现完整平时练习只写基础版没扩展时间分配差简答题写太多设计题留白没有先扫卷子按分值分配时间业务结合弱首屏优化方案全是通用手段没提途虎门店场景对目标公司的业务理解不足第一项我觉得是最亏的。Vue2 的响应式缺陷我在很多文章里都看过但笔试时答得太粗写的是“检测不到数组变化”其实更准确的说法是“通过索引修改数组元素时检测不到”而push、pop等方法是通过重写数组方法能检测到的。这种细节上的差别恰恰是阅卷人判断你“真懂”还是“背过”的关键。7.2 时间分配的实操策略90 分钟的卷子我后来总结出一个比较合理的分配方式前 10 分钟把整张卷子扫一遍把会的题用铅笔标记出来先做分值高的简答和手写选择和填空放在最后。这样即使时间不够也不至于为了两分的选择题丢了一道十五分的设计题。不过这个策略有个前提你必须对选择题涉及的基础知识非常熟练“扫一眼就知道答案”的程度。如果选择题还需要大量心里推导说明基础还不够扎实这种时候反而要按顺序做因为选择题里的考点往往能帮你在后面的大题里建立思路。7.3 考完试后的延伸准备笔试只是第一关途虎的面试一般会接着笔试内容深挖。我后来发现笔试里的简答题和设计题几乎都会被面试官原封不动地拿来做追问比如你手写了 Promise.all那 Promise.race 怎么实现allSettled 呢你说 Vue3 用 Proxy 代替 defineProperty那 Proxy 的兼容性怎么解决你做了骨架屏那骨架屏本身怎么保证不闪白你选 Vite 做活动页那 Webpack 项目怎么迁移到 Vite所以我的建议是笔试结束之后不要立刻把题目忘掉而是把每一道题当成一个“面试问题种子”自己去扩展三到五个相关问题。准备充分之后一轮面试的时候会轻松很多。8. 写在最后的个人体会这套卷子做完我最强烈的感受不是题目难而是它能看出来公司真的在找什么样的人。途虎的业务很重场景、很看细节所以试卷不考花哨的冷门知识点而是把大量精力放在基础是否扎实、代码能不能写出来、方案能不能落地这几件事上。我后来在准备其他公司笔试时也一直沿用这套卷子的复盘思路先通读整卷判断分值分布简答题尽量分层作答用“先说结论、再展开原理、最后补边界条件”的结构手写题一定要把边界情况写出来设计题一定要结合目标公司的业务场景而不是写通用模板。这一套方法在秋招后期帮我拿到了好几个大厂的笔试通过通知算是这份卷子给我留下的最大财富。最后分享一个小技巧笔试时遇到手写题不管能不能完整写出来先把代码的主流程用注释写清楚比如// 1. 收集参数 // 2. 返回 Promise // 3. 处理成功和失败。这样即使代码不完整阅卷人也能一眼看到你的思路按照关键步骤给分。我就是靠这个习惯在一道没完全写对的算法题上拿到了大部分步骤分。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价