1. 秋招笔试的定位不是筛代码是在筛基础2023年用友集团秋招前端岗的笔试整体给我的感觉就八个字基础扎实覆盖面广。和很多互联网大厂动辄上来就问你“如何设计一个高并发IM系统”的作风不同用友的笔试题更像是一张“前端工程师基础能力体检表”它不追求你有多炫技但要求你把前端这摊子事儿里的地基打得足够牢。笔试形式是线上限时完成全程摄像头监控题型分为**单选、多选、编程题含手写代码**三大部分总时长我记得是90分钟。时间分配上客观题大概占了60%的题量但分值比重和编程题基本五五开。也就是说就算你选择题能全对编程题如果一道都AC不了大概率也是无缘面试的。为什么会这样设计后来我复盘了一下用友的业务线很宽从财务软件到人力管理再到云服务前端承载的是大量企业级中后台系统。这类系统的特点是什么是逻辑复杂、状态多、需要长期维护。所以他们在筛选候选人时首要考察的不是你做过多少花哨的H5动画页面而是你对JavaScript语言特性的理解深度、对前端工程化的基本认知、以及对常见数据结构在实际业务中的运用能力。这其实给准备笔试的同学提了个醒别只看面经里的偏题怪题回归基础才是王道。我整理了一下本次笔试涉及的知识点分布大致可以归为四类知识模块具体考察点出现形式JavaScript核心原型链、闭包、事件循环、this指向、数组方法实现单选、多选、手写CSS与布局盒模型、两栏布局、flex/grid特性、BFC单选、手写浏览器与网络缓存机制、HTTP状态码、跨域、渲染原理单选、多选前端框架与工程化Vue生命周期、组件通信、Webpack基础配置单选、简答看到这个分布你就明白了用友的笔试几乎不考“最新技术”。像什么TS类型体操、Next.js、WebAssembly这些一道都没出现。它就是老老实实地考你“一个前端工程师入行必须具备的常识”。2. 客观题里那些容易踩坑的知识点2.1 单选看似简单实则暗藏陷阱先说单选部分。用友的选择题难度梯度做得挺明显前几道属于送分题比如“以下哪个不是HTTP请求方法”这种但做到中间开始出现一些让人纠结的题目。举个我印象很深的例子console.log([] ![]);这题考的是隐式类型转换。[]首先被转为Boolean类型因为![]的优先级更高![]的结果是false。然后比较[] false[]转成原始值会变成空字符串false转成数字是0空字符串转成数字也是0所以最终结果是true。这道题说难不难但如果你对ToPrimitive的转换规则不熟很容易凭直觉选false。我备考时刷过大量类似的题目所以一眼就扫过去了但相信现场有不少同学在这里卡了一会儿。另一个值得说的考点是Promise的执行顺序async function test() { await Promise.resolve(); console.log(a); } setTimeout(() console.log(b), 0); test(); console.log(c);输出结果是c、a、b。await后面的代码会推入微任务队列而setTimeout的回调属于宏任务。事件循环每执行完一个宏任务后会清空整个微任务队列。所以同步代码先打印c然后微任务打印a最后宏任务打印b。这道题本质是考察事件循环的优先级。如果你只是背了“微任务先于宏任务”这个结论但不知道await的底层实现是Promise.resolve().then()就很容易把a和c的顺序搞混。2.2 多选少选漏选都是痛多选是这次笔试中失分最惨烈的部分因为它的规则是“少选得一半分多选、错选不得分”。这导致一个很尴尬的局面你明明知道某个选项是对的但怕选多了扣分最后只能保守地选一两个。有一道题问的是“哪些方法会改变原数组”。选项给了push、map、splice、slice、sort。正确答案是push、splice、sort。这道题几乎属于常规操作但有个细节要注意splice和slice只差一个字母前者会改变原数组后者返回新数组。这种题目就是典型的“基础不牢地动山摇”。还有一道关于**CSS 中哪些属性会触发重排reflow**的多选。选项包括width、transform、opacity、font-size、visibility。正确答案是width和font-size。但很多同学会把opacity也算进去因为它确实会触发重绘。这里需要区分清楚重排Layout改变元素的几何属性如宽高、位置、字体大小。重绘Paint改变元素的视觉属性如颜色、透明度、阴影。transform和opacity在现代浏览器中会走合成层既不触发重排也不触发重绘严格说是触发合成。这种概念的辨析就是多选题的高频考点。2.3 简答框架原理不能只停留在“会使用”这次笔试里还有一个简答题问的是“Vue 3 中computed和watch的区别以及各自的使用场景”。这个问题如果只是背答案——computed有缓存、watch没有缓存——那就只能拿基础分。但如果能往深处写两笔比如说明computed内部通过effect函数实现惰性求值和缓存依赖变化时通过dirty标志位控制是否需要重新执行getterwatch则是通过traverse深度遍历依赖且可以在回调中获取新旧值分数就会明显不一样。我当时的写法是computed适用于“由已知数据推导出新数据”的场景比如购物车的总价它依赖的商品价格变了总价才需要重新计算watch适用于“数据变化时需要执行异步操作或开销较大的操作”的场景比如搜索框输入监听关键词变化后发送请求。computed是惰性的只有当它的依赖发生变化并且自身被访问时才会重新求值watch是即时的依赖一变就执行回调。computed返回的ref对象只读watch允许你在回调里自由地修改其他状态。这种题目没有唯一的标准答案但分点作答、逻辑清晰是拿高分的关键。3. 编程题解析从URL解析到深拷贝全是“手写题”的老演员用友的编程题一共三道两道纯手写一道是算法题。整体难度偏向中低档但非常考验边界情况的处理。3.1 实现一个getUrlParams函数这道题的要求是给定一个URL字符串解析出其中所有的查询参数并返回一个对象。如果同一个key出现多次则合并为数组。这道题本身难度不大关键在于你是否能处理以下边界情况URL 中可能没有查询参数返回空对象参数值可能包含号表示空格中文编码的%E4%BD%A0%E5%A5%BD需要解码同一个key出现两次以上需要合并成数组我的答案用了URLSearchParams这个原生API但为了保险起见也补充了手写兼容方案function getUrlParams(url) { const params {}; const search url.split(?)[1]; if (!search) return params; const parts search.split(); for (const part of parts) { if (!part) continue; const [key, value] part.split(); const decodedKey decodeURIComponent(key); let decodedValue decodeURIComponent(value || ); // 处理 号表示空格的情况 decodedValue decodedValue.replace(/\/g, ); if (params[decodedKey] ! undefined) { if (Array.isArray(params[decodedKey])) { params[decodedKey].push(decodedValue); } else { params[decodedKey] [params[decodedKey], decodedValue]; } } else { params[decodedKey] decodedValue; } } return params; }这道题考的不是你会不会用API而是你有没有考虑到真实业务中可能出现的脏数据。很多人只写了简单版遇到重复参数就直接覆盖了这在笔试里肯定是要扣分的。3.2 手写deepClone但要求能处理循环引用第二道手写题也属于经典中的经典深拷贝。如果只是浅拷贝或简单递归版本大概率会被判分不高。这道题真正的考点是循环引用。常规的递归实现一旦遇到对象里循环引用obj.self obj就会无限递归导致栈溢出。解决方案是用一个WeakMap来存储已经遍历过的对象function deepClone(target) { const map new WeakMap(); function clone(value) { if (value null || typeof value ! object) { return value; } if (map.has(value)) { return map.get(value); } const result Array.isArray(value) ? [] : {}; map.set(value, result); for (const key of Object.keys(value)) { result[key] clone(value[key]); } return result; } return clone(target); }这里有个细节WeakMap和Map的区别是什么WeakMap的键是弱引用不会阻止垃圾回收。在深拷贝这个场景下用WeakMap是更合理的因为如果原对象被回收了我们用来存储拷贝关系的临时Map也不应该继续占用内存。为什么我说这道题要答得漂亮因为深拷贝几乎是所有大厂笔试的“必考题型”它考察的不仅是递归逻辑还有对JS内置对象的理解、对边界情况的处理、以及对内存管理的认知。用友把这道题放进笔试基本也是想看看候选人有没有做过类似的完整训练。3.3 算法题最长无重复字符子串这是一道经典的滑动窗口题。给定一个字符串找出其中不含重复字符的最长子串的长度。比如abcabcbb输出3。直接暴力解法的复杂度是O(n^2)在笔试中虽然能通过大部分测试用例但如果你能写出O(n)的滑动窗口解法肯定会加分。function lengthOfLongestSubstring(s) { let maxLen 0; let left 0; const map new Map(); for (let right 0; right s.length; right) { const char s[right]; if (map.has(char)) { // 出现重复字符时左指针跳到重复字符的下一个位置 left Math.max(left, map.get(char) 1); } map.set(char, right); maxLen Math.max(maxLen, right - left 1); } return maxLen; }实际上用友笔试里的算法题很难超过LeetCode的“中等难度”。去年的题目集中在数组操作、字符串处理、简单动态规划这三类。所以刷题阶段重点突破LeetCode的Hot 100和力扣的“剑指Offer”系列就够了没必要在“困难”级别的题目上死磕。4. 笔试题背后的“用人逻辑”用友到底想要什么样的前端笔试做到一半时我就在想用友的题目为什么会这样出跟字节、腾讯的笔试题相比它少了那种“极具挑战性”的压迫感多了一份“务实”的气质。后来跟一位用友内部的前端朋友聊过之后我得出一个结论用友招前端要的不是“创意体验工程师”而是“企业级应用开发工程师”。企业级应用有几个明显的特点:第一生命周期长。一个ERP系统的前端代码可能要用十年以上这就要求开发者的代码写得规范、可维护、注释清楚。所以笔试中会考察你对var和let的区别、对常量不可变性、对纯函数的理解本质上都在看你写出的代码是否“稳”。第二状态管理复杂。中后台系统的页面往往充斥着大量表单、弹窗、列表、权限控制。一个角色切换、一个状态漂移都可能引发连锁bug。所以笔试题中会出现computed和watch的区别、事件循环的顺序、深拷贝的实现这些都是为了验证你是否能驾驭复杂状态流。第三跨部门协作频繁。前端往往要和后端、测试、产品、运营打交道。你需要看懂接口文档需要能处理各种异常返回。那两道关于URL解析和参数转换的题其实就是在模拟日常工作场景——后端返回的字段格式千奇百怪甚至有空值、嵌套、数组你能不能写出健壮的解析函数所以如果你打算投用友的前端岗我认为备考重心应该放在JavaScript 核心概念原型、闭包、模块化、异步编程。这是重头戏怎么强调都不为过。布局与样式基础虽然现在组件库盛行但不代表你可以不懂底层布局。Flex、Grid、BFC、经典两栏三栏布局一定要能脱口而出。浏览器工作机制从输入URL到页面渲染的完整过程、HTTP缓存、跨域解决方案。这是前端和浏览器打交道的底层逻辑。算法与数据结构基础数组和字符串的常见操作、栈和队列的应用、简单的动态规划。5. 关于笔试的备考策略与踩坑复盘作为过来人我想分享几个具体的备考建议。5.1 提前熟悉线上笔试环境这次用友的笔试用的是特定在线测评系统页面左侧是题目列表右侧是代码编辑器。编辑器不支持自动补全也没有浏览器控制台可以调试。我因为之前用惯了VS Code在裸编辑器里写代码时好几次出现document.getElementById这种低级拼写错误。建议你提前一两周去牛客网或力扣的“模拟笔试”功能练几套题让自己适应“没有智能提示”“不能在线调试”的原始编码节奏。这个适应成本远比想象中高。5.2 合理分配时间我在笔试时给自己的时间预算是客观题45分钟编程题45分钟。但实际上客观题里有好几道多选题让我纠结了很久导致后面编程题时间被压缩到只剩30分钟。第三道算法题我虽然思路很快但写完之后连测试用例都没跑完就到了交卷时间。这里复盘一下客观题一旦超过2分钟还没确定答案就应该果断蒙一个并标记下来等所有题目做完之后如果有剩余时间再回头检查。笔试和日常写代码不一样先保证完成率再追求正确率。一道分值2分的多选题花10分钟去做在策略上是不划算的。5.3 编程题一定要写“防御性代码”也就是说不要只满足于“能跑通测试用例”的答案还要考虑异常输入。比如实现getUrlParams时如果URL里没有查询参数怎么办如果参数没有号怎么办如果值是undefined怎么办这些情况在笔试时可能不会全部出现在测试用例里但面试官在看代码时一定会注意你有没有处理。编写防御性代码不是过度设计而是一个前端工程师的基本素养。你在笔试里写了这些边界判断面试官就能判断你平时写代码时大概率也是一个注重细节的人。5.4 刷题之外的“软实力”用友的笔试通过后会进入约面环节后续的面试中通常会针对笔试的错题进行追问。所以笔试结束后不是说就万事大吉了建议你把当时拿不准的题目记下来逐一查漏补缺。比如选择题里出现的一个不太确定的正则表达式或者某个多选里漏选的CSS属性都值得在笔试结束后弄明白。我舍友去年投用友笔试中有一道关于flex的题目选错了结果一面时面试官直接问“你笔试里那道flex题为什么选那个选项”这就是在考察你的“复盘能力”和“学习态度”。6. 写在最后笔试是一面镜子2023年用友秋招前端笔试给我的最大感受是它不像一张刁钻的考卷更像一面镜子把你的基础水平照得一清二楚。如果你在笔试中发现自己对原型链、事件循环、深拷贝这些基础概念很模糊那大概率不是备考不足而是平时写代码时就没有真正深入到底层去思考。如果你正在备战类似的校招笔试我的建议很直接把《JavaScript高级程序设计》里关于对象、原型、异步的章节反复读透把力扣热题100里的数组和字符串题刷两遍每道题都要求自己能白板写出完整解法把深拷贝、防抖节流、数组去重、Promise.all这些手写题全部手敲一遍不能只看答案考场上先做会做的再抠纠结的编程题至少保证拿到一半的分数用友的笔试只是一个开始后面还有群面、技术面、HR面。但如果你能把这套笔试题目吃透我敢说其他以基础考察为主的公司很多传统行业转型互联网的企业笔试题你也能应对个七七八八。基础这东西永远是前端路上最划算的投资。