
前阵子整理旧电脑翻到自己当年备战校招时存的一堆笔试题其中就有一套用友2018年的web前端笔试题。虽然时间过去好几年了但前端笔试的核心考点其实变化不大——JavaScript基础、CSS布局、浏览器机制、手写代码这些至今仍然是校招面试的高频区。把这套题重新过了一遍发现里面好几道题放到现在依然有很强的参考价值值得拿出来逐题拆一拆。这篇文章我打算按照当年的做题思路来组织先聊聊这套题整体在考察什么再逐类拆解核心题目和背后的知识点然后分享一些实战的做题策略和常见丢分点。不管你是正在准备校招的应届生还是想查漏补缺的初级前端这篇都能帮你摸清大厂笔试题的出题套路。1. 用友这套题到底在筛什么人1.1 岗位画像与笔试筛选逻辑先说个比较现实的问题校招笔试不是用来招天才的而是用来做减法、滤掉基础不牢的候选人的。用友作为老牌企业管理软件厂商它的前端团队主要做B端业务系统这类系统的特点是页面复杂、交互多、兼容性要求高、维护周期长。所以笔试题目天然偏向基础扎实、逻辑清晰、能写干净代码的人。看完这套题的整体结构你能明显感觉到出题人不是想考你多新潮的技术而是想确认三件事第一你的JavaScript基础到底扎不扎实第二你写页面的时候是真的理解布局原理还是只会拖组件第三你遇到问题的时候有没有一套清晰的排查思路。这套考题基本就是围绕这三个维度来设计的。另外B端业务的特殊性决定了他们特别看重代码的健壮性和可维护性。笔试题里关于变量提升、闭包、事件循环这些知识点的考察本质上是在测试你对语言运行机制的理解深度。一个能准确说出闭包内存回收机制的人和只知道“闭包就是函数套函数”的人写出来的代码质量是完全不同的。1.2 试卷结构的高频模块分布基于这套题的整体印象我重新梳理了一下涉及的考察模块。为了让你更直观地看出重点在哪里我做了一张分布表。考察模块所占比重典型题型考察目标JavaScript基础约35%变量提升、闭包、原型链、this指向语言理解深度CSS与布局约25%浮动、居中方案、盒模型、Flexbox页面实现能力浏览器与网络约20%事件循环、事件冒泡、缓存、HTTP状态码整体运行机制认知手写代码与逻辑约20%数组去重、防抖节流、递归等实际编码能力这份分布很有代表性。JavaScript基础占比最高而且考察的不是单纯背概念而是让你看代码写输出结果。这类题最考验功力因为你能背出“闭包是函数和其词法作用域的组合”这句话一点用都没有你得能判断出一段具体代码运行时会发生什么。CSS布局那部分也很有意思不是问你“Flexbox有哪些属性”而是给你一个具体的页面效果图让你用几种不同的方案实现同样的布局。这种考法更像是在模拟真实的开发场景——毕竟实际工作中没人会问你flex-grow的默认值是多少但经常需要你实现一个复杂的页面布局。2. 核心题目拆解与知识点解析2.1 JavaScript基础变量提升与闭包JavaScript基础部分有一类非常经典的题目就是给你一段代码让你写出运行结果。这类题目考察的核心是变量提升和函数提升。我记得当时有一道题大概长这样var a 1; function foo() { console.log(a); var a 2; } foo();如果你回答输出1那就掉坑里了。正确答案是undefined。原因在于在函数foo内部var a的声明会被提升到函数作用域的顶部但赋值不会提升。所以函数内部实际的执行逻辑是function foo() { var a; // 声明提升此时a是undefined console.log(a); // 输出undefined a 2; // 赋值 }这道题特别经典因为它考察的是你对JavaScript作用域和执行机制的理解而不是简单的记忆。很多人在平时的开发中不太会写出这种代码但这道题能很好地区分“背过概念”和“真正理解引擎行为”的候选人。接着就是闭包几乎每一套前端笔试题都会有一道闭包相关的题。常见的是让你解释循环中使用闭包的陷阱for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); }如果你知道正确答案是连续输出5个5而不是0到4说明你对闭包和异步执行顺序是有概念的。因为var声明的i是函数作用域的循环结束后i已经变成了5当setTimeout的回调函数执行时它通过闭包引用的是同一个i。如果要让它输出0到4可以用let声明块级作用域或者用立即执行函数把每次循环的i值保存下来。我当年复习这类题的时候总结了一个比较笨但很有效的方法凡事遇到setTimeout、setInterval、Promise混在一起让你输出顺序的题先在草稿纸上画出“调用栈、任务队列、微任务队列”三列再一步一步模拟执行。这个方法虽然慢但对理解事件循环和异步机制特别有效。2.2 原型链与this指向原型链的考察方式通常有两种一是直接问你某个对象查找属性的顺序二是让你手写一个简单的继承。这道题的常见表现形式是function Person(name) { this.name name; } Person.prototype.sayName function() { console.log(this.name); }; var p1 new Person(Tom); var p2 new Person(Jerry); p1.sayName(); // 输出什么 p2.sayName(); // 输出什么 console.log(p1.sayName p2.sayName); // 输出什么这里前两个输出结果都是各自的name分别是Tom和Jerry。第三行输出true因为sayName方法定义在Person.prototype上p1和p2通过原型链找到的是同一个函数。这正好引出了通过原型链实现方法共享的核心思想。关于this指向我建议直接记住一条规则函数中的this在调用时确定谁调用了它this就指向谁。严格模式下独立调用的函数内部this是undefined非严格模式下会指向全局对象。箭头函数则完全不同它没有自己的this沿用的是定义所在作用域的this。原型链和this这两块知识笔试考的是选择题和简答题面试就喜欢让手写继承。不管怎么考关键是理解new关键字到底做了什么创建一个新对象、把新对象的原型指向构造函数的prototype、把构造函数的this绑定到新对象上、如果构造函数没有显式返回对象就返回这个新对象。2.3 数组方法详解与手写实现用友这套题里数组相关题目出现的频率很高这也是为了考察数据处理的实战能力。面对一道数组题我的判断依据是这道题有没有更合适的数组方法可以用是选择map、filter、reduce这类函数式方法还是用for循环处理复杂逻辑。比如数组去重的题目我印象中有这样的典型考查var arr [1, 2, 2, 3, 4, 4, 5]; console.log([...new Set(arr)]);这道题今天看起来很简单但当年ES6的Set刚普及不久能写出这一行的候选者说明对ES6特性是熟悉且有意识在用的。如果要求不用Set也可以借助一个空对象来记录已出现的属性达到去重效果。还有个常见题是数组扁平化考查点在于递归和reduce的使用。如果要求不依赖flat()方法可以手写递归遍历碰到子数组就继续展开最终拼接成一个新数组。这个题的进阶版本是要求用深度参数控制展开层数写起来就更有意思了。数组题我建议不要只停留在“会调API”尽量去理解每个方法内部的行为比如map和forEach的区别、reduce的初始值是否传对、sort的默认字典序陷阱等。曾有道题问[10, 5, 40, 25].sort()的运行结果如果不传比较函数JavaScript会把元素先转成字符串再按字典序排序结果就是[10, 25, 40, 5]这个陷阱当年骗了不少人。2.4 CSS布局水平垂直居中与经典布局方案CSS部分最能拉开差距的就是“水平垂直居中”这种看似简单实则多样的题目特别适合拿来当作基础考察题。套在这套题里它通常以“请用至少三种方式实现一个元素的水平垂直居中”的形式出现。我当时总结了5种常用方案每种方案的适用场景和原理都不同使用flex布局父容器display: flex子元素margin: auto简单高效使用position transform父容器相对定位子元素绝对定位配合translate(-50%, -50%)适合已知父容器尺寸的场景使用position 负margin子元素绝对定位后设置top/left为50%再用margin-top和margin-left拉回自身尺寸的一半要求子元素宽高固定使用table-cell父容器display: table-cell vertical-align: middle text-align: center适合老项目兼容方案使用grid布局place-items: center代码最短但需要考虑浏览器兼容性这道题不出现在笔试题里我还真不习惯。从功能角度看既能考察你对常用布局属性的掌握程度也能看出你面对同一个问题时能否提供多个解决方案。除了居中经典的还有左侧固定宽度、右侧自适应的两栏布局。常见的实现方式有左侧float右侧margin-left老办法、父容器flex右侧flex:1现代推荐、左侧绝对定位右侧margin-left适合左侧高度固定的场景。实际项目中还经常要求左右两侧等高这就涉及到flex的align-items属性的默认值问题需要注意。2.5 浏览器机制事件流与事件委托浏览器相关题目经常考事件流。要知道事件在DOM中传播分为三个阶段捕获阶段、目标阶段、冒泡阶段。笔试题里最常见的就是让你判断给定代码的输出顺序div idparent button idchild点击/button /div script document.getElementById(parent).addEventListener(click, function() { console.log(parent冒泡); }); document.getElementById(parent).addEventListener(click, function() { console.log(parent捕获); }, true); document.getElementById(child).addEventListener(click, function() { console.log(child); }); /script这里点击child的时候输出顺序是parent捕获、child、parent冒泡。因为捕获阶段从document根节点向下传播到目标节点先经过parent的捕获监听器然后到达目标节点执行目标监听器最后进入冒泡阶段从目标节点向上传播回根节点再次经过parent时触发冒泡监听器。事件委托也是一个高频考点它的核心思想是利用事件冒泡将子元素的事件处理委托给父元素来绑定。这样做的好处有两个一是可以处理动态添加的子元素二是减少事件监听器的数量提升性能。笔试题常见的考法是要求你给一个列表的每一项绑定点击事件并获取对应索引而实际开发中我还观察到很多人一提到事件委托就只能想到click其实scroll、mouseover、keydown等事件也都可以做委托。2.6 手写代码防抖节流与对象深拷贝这部分通常作为压轴题出现它的存在是为了考察候选人脱离工具库后能不能用原生JavaScript解决实际问题。用友这套笔试题里我印象比较深的是防抖和节流的手写实现。它们都是性能优化的重要手段很多初级开发知道概念但写不出完整可用的函数。先说说防抖它的思想是触发事件后延迟执行如果在延迟期间又触发了事件就重新计时。典型场景是搜索框输入用户停止输入后500毫秒才发起请求。function debounce(fn, delay) { let timer null; return function() { const context this; const args arguments; if (timer) { clearTimeout(timer); } timer setTimeout(function() { fn.apply(context, args); }, delay); }; }节流的思想则相反不管触发多频繁保证在固定时间间隔内只执行一次。典型场景是滚动事件监听、窗口resize避免在短时间内反复进行高消耗操作。function throttle(fn, interval) { let previous 0; return function() { const context this; const args arguments; const now Date.now(); if (now - previous interval) { previous now; fn.apply(context, args); } }; }除了防抖节流手写深拷贝也几乎成了标配。考察的核心点是如何区分数组和普通对象如何处理循环引用以及是否需要处理Date、RegExp等特殊类型。如果笔试只要求基础版本处理好数组和对象的情况基本就能过关。想要更完整的话可以加一个WeakMap缓存来实现循环引用检测这样就算对象里存在互相引用也不会出现死循环。3. 做题顺序与时间分配策略3.1 拿到试卷先做三件事校招笔试的时间通常都挺紧张尤其是同时包含选择题、填空题和手写代码题的综合卷。用友这套题的总体量不算小我当年做的时候最大的感受是如果按顺序从头做到尾很容易卡在中间某道难缠的题上导致后面的代码题没时间写。所以开场不要急着做我的习惯是先花三到五分钟把整份试卷扫一遍做三件事第一快速判断题型分布和分值占比心里有数哪些题值得花时间。第二先看一眼手写代码题因为它可能在你不注意的时候已经在潜意识里酝酿解法了等做到那题时会更快有思路。第三标记出自己不太有把握的题目先跳过保证能拿的分全部拿到手。这个方法其实有点像周赛做题的战术本质是时间管理。笔试看的是整体得分不是单题MVP一道不会做的题纠结15分钟不如把后面三道会的题稳稳拿下。3.2 各题型的时间预算与取舍根据我多年实战和身边同学的反馈我整理了一份针对类似前端笔试题的时间分配参考表你可以结合试卷实际分值和难度微调题型建议用时策略选择题/判断题10-15分钟读完题就要有答案超过1分钟没把握就先标记跳过填空题/读代码写结果25-30分钟这类题最考基础用草稿纸模拟执行别凭感觉猜CSS布局/简答题15-20分钟能画图说明就画图保持条理清晰手写代码题30分钟以上留足时间先写主流程再补细节函数签名和边界条件也要写完整关于取舍有一条原则我特别想分享宁可把一道手写代码题写得完整、有注释、考虑边界也不要为了多写半道题而在每道题上都留个半成品。因为面试官看你的手写代码时更看重的是思路清晰度和代码的完整性。3.3 手写代码时的答题规范这部分特别容易被忽略但恰恰是很多人的失分点。手写代码题虽然不需要真正跑起来但你写的代码要尽可能接近能直接运行状态。我总结了几个约定俗成的注意事项代码首先要缩进清晰变量命名要让读者一看就懂不要写一个字母的变量名除非是for循环的临时变量。其次写函数时先在脑中过一遍三种情况正常情况、空值情况、边界情况。比如手写数组去重时要考虑输入是空数组怎么办手写防抖要考虑不传delay参数怎么办。最后一点建议是如果时间允许在代码里写上简单的注释说明你的关键步骤是在做什么。面试官一天要批几十份试卷一份清爽、有条理、有注释的代码即使不是最优解法也会留下更好的印象分。这些细节实际工作里写代码也一样重要。4. 备考思路与常见失分点复盘4.1 五个容易丢分的题目类型这些年帮不少朋友模拟过笔试题也搜集过很多同学的真实反馈整理出了五个最容易丢分的题目类型。你把这几类练熟了基本就能覆盖大部分校招前端笔试的坑。第一个就是读代码写输出结果题尤其是涉及变量提升、this指向和异步执行顺序的。这类题丢分的原因通常不全是“不会”而是凭直觉猜答案没有在草稿纸上认真模拟执行过程。我的建议是平时练习时就要养成手写执行流程的习惯把每一行的调用关系理清楚。第二个是CSS居中布局题。常见误区是只会一种flex方案一旦题目附加了兼容性条件就束手无策。多准备几种不同思路的方案无论对笔试还是面试都很有帮助。第三个是事件委托相关题目。很多人只知道概念但动手写代码时忘了处理事件参数和指定选择器匹配的逻辑导致代码不完整或逻辑不严谨。第四个是数组相关题尤其是sort的字典序陷阱、map与parseInt组合的坑。这类题一旦踩坑几乎全军覆没但又是平时开发中经常遇到的小陷阱。第五个是手写代码题的边界处理。整体逻辑写对了但没考虑参数为空、输入类型不对、返回值格式不对等情况被扣了细节分非常可惜。4.2 针对校招刷题的高效复习清单关于如何准备这类笔试题我结合自己当年的复习路径和后来面试新人的经验整理了一个清单按优先级排列JavaScript核心概念变量提升、作用域、闭包、原型链、this指向、事件循环、深浅拷贝这些是重中之重每一个都要既能说清原理又能手写演示。ES6常用特性let/const、箭头函数、解构赋值、模板字符串、Set/Map、Promise、async/await要做到看到代码就能说出行为。CSS布局能力盒模型、浮动、定位、Flexbox、Grid每种布局至少能手写两到三种方案。浏览器机制事件流三个阶段、事件委托、垃圾回收、存储方式localStorage/sessionStorage/cookie的区别、常见的HTTP状态码和缓存策略。手写算法与工具函数数组去重、扁平化、防抖节流、深拷贝、数组去重、字符串翻转、简单排序这些是校招笔试的最高频手写题。4.3 从笔试成绩反推知识体系的薄弱环节如果你做过一套题之后感觉不太理想别急着否定自己也别急着再去刷十套新题。我建议反过来做一次“错题归因”这比题海战术高效得多。把错题分分类你是基础概念题错得多还是代码阅读题错得多或者手写实现题写不出如果是基础概念错说明你对语言机制的理解还是偏表面的这种只能靠系统性回炉看看经典的JavaScript教程把执行上下文、作用域链这几章反复读。如果是代码阅读题错说明你读代码量不够可以去GitHub上随便打开一个开源项目刻意去读那些你没有写过的代码遇到不理解的就断点调试。如果是手写实现题写不出说明平时动手少这个没有捷径就是练把高频手写题逐个做到能默写。我见过很多同学准备笔试时有个误区就是疯狂搜集各种面经和题库但大部分题稍微换个形式又不会了。说到底笔试考的是基本功的扎实程度而不是见过多少题。一套经典题目能做到融会贯通比走马观花看十套题有效得多。4.4 这套题对实际开发的启发从这套题往回想其实很多考点在真实的用友B端开发场景中都有对应。比如原型链和闭包写老旧的jQuery插件或者维护历史遗留代码时必须得过这一关比如事件委托后台管理系统的动态表格里每行都要绑定操作事件直接给每行加监听器会产生大量冗余监听函数事件委托在这种场景下几乎就是最优解再比如防抖节流搜索框自动补全和列表滚动加载这两个场景你几乎每天都会用到。我个人的体会是笔试虽然看起来像是“纸上谈兵”但它在很大程度上能反映一个前端工程师的日常积累。你在实际开发中养成的思维习惯和踩坑经验恰恰就是做这些笔试题时的隐形优势。所以如果你想在校招笔试中拿高分与其临时抱佛脚刷题不如平时多思考“为什么这样写”把每一次报错和排查都变成你知识体系的一部分。最后分享一个我用着很顺手的复盘小技巧每次做完一套笔试题不要只对答案而是把所有错题整理成一个Markdown文件按照“题目—我的错误答案—正确答案—错误原因—知识点链接”五个字段记录。坚持整理几套之后你会发现自己经常在同一个知识点上重复犯错这时候重点突破就有了依据。这个方法也推荐给你。