ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

用友校招笔试题解析:前端基础与工程思维全复盘

用友校招笔试题解析:前端基础与工程思维全复盘 1. 用友校招笔试题的考察逻辑这不是一道题而是一套筛选漏斗老规矩先聊聊这份题出现的背景。用友作为国内老牌的企业服务厂商它的校招笔试题一直有个鲜明特点不追求算法题刷到天昏地暗而是重点关注基础功、编码习惯和知识面的广度。2017年这个时间点尤其特殊——前端工程化刚刚开始普及Vue 2.0 发布不到一年React 16 还没出来ES6 已经大面积落地但好多应届生还在写 var。所以这份笔试题二虽然在今天看来有些基础题已经成了常识但它的命题思路和考察维度对现在准备校招、社招的人依然有很强的参考意义。整套笔试题的考察逻辑可以拆成三个层次。第一层是语言基础主要是 JavaScript 的核心机制比如作用域、闭包、原型链、this 指向这种跑不掉的考点这一层筛掉的是背过 API 但没理解原理的人。第二层是浏览器与页面开发涉及 DOM 操作、事件机制、CSS 布局与兼容性这一层筛掉的是只会在框架里写业务脱离框架就不知道怎么干活的人。第三层是综合能力典型的表现形式是一道需求描述模糊的编程题你不仅要写对还要考虑健壮性、扩展性这一层筛掉的是能跑就行从不思考工程质量的人。有趣的是用友笔试题并不是纯粹意义上的难它考的很多是你在实际项目中经常遇到、但未必深究过的细节。比如你天天用 addEventListener但你有没有想过同一个元素上绑定的多个事件按什么顺序执行你天天写 CSS 实现水平垂直居中但让你列出三种以上方案并说明每种方案的适用场景你会不会突然卡壳这种题考察的不是刷题量而是有没有真正用心写过代码。这篇文章我就以笔试题二这份卷子为主线把它可能覆盖的知识点和实际开发中的关联做一次完整复盘。不管你是正在准备校招的应届生还是想自查基础功是否扎实的在职前端这份拆解都值得花几分钟过一遍。2. JavaScript 核心考点this 指向、闭包与原型链的连环追问2.1 this 指向题四种调用方式一个都不能混笔试题里最常见的 with 系列题之一就是给出几段代码问你输出是什么然后解释为什么。这类题的底层逻辑其实就是 this 的四种绑定规则默认绑定非严格模式下独立函数调用 this 指向 window浏览器环境严格模式下 this 为 undefined。隐式绑定作为对象方法调用时this 指向该对象。显式绑定通过 call、apply、bind 调用时this 指向传入的第一个参数。new 绑定通过 new 调用构造函数时this 指向新建的实例对象。优先级上new 绑定 显式绑定 隐式绑定 默认绑定。实话说第一条和第四条大部分人都记得牢容易翻车的是隐式绑定和显式绑定的组合题以及隐式绑定丢失这个经典陷阱。var name global; var obj { name: obj, getName: function() { return function() { return this.name; }; } }; console(obj.getName()()); // 输出什么这段代码输出 global。原因很简单obj.getName()返回的是一个普通函数当这个函数被独立调用时this 已经和 obj 没有任何关系了走的是默认绑定规则。很多人在这里会下意识认为闭包会保留 this 的指向这是完全错误的。闭包保留的是变量作用域不是 this 绑定。如果面试官继续追问怎么让它输出 obj答案无非是三个在外层var self thisES6 之前最常用的方式或者用箭头函数箭头函数没有自己的 this继承定义时的词法作用域或者返回前显式.bind(this)。这个考点在实际项目里的映射特别典型。比如你写一个 Vue 组件在 methods 里用了setTimeout或者注册了某个回调函数如果回调用的是普通 function里面的 this 就会丢。2017 年那会儿好多刚从 jQuery 转 Vue 的开发者都在这个问题上踩过坑——jQuery 里大量使用回调函数this 又频繁被 jQuery 内部改写指向 DOM 元素和 Vue 的 this 语义完全不同。所以这道题考的不是一个孤立的语法知识点而是你能不能理解 this 是函数调用时确定的而不是定义时确定的 这句话的真正含义。2.2 闭包会做题只是入门能说清实践价值才叫懂闭包题基本上所有前端笔试题的标配用友也不例外。典型题目是给出一段循环加 setTimeout 的代码for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 100); }输出结果是五个 5这个大多数人都知道。但笔试题的进阶版往往会要求你能写出修复方案并说明每种方案背后的机制。修复方式至少有三种。第一种是立即执行函数 传参for (var i 0; i 5; i) { (function(j) { setTimeout(function() { console.log(j); }, 100); })(i); }这相当于在每次循环时创建了一个新的函数作用域setTimeout 回调通过闭包捕获了每次传入的 j。第二种是 ES6 的 letfor (let i 0; i 5; i) { setTimeout(function() { console.log(i); }, 100); }let 在 for 循环的块级作用域中每次迭代都会创建一个新的绑定回调闭包捕获的是当次迭代的 i。第三种是 bind for (var i 0; i 5; i) { setTimeout(console.log.bind(console, i), 100); }这个方案其实挺取巧的直接把 i 作为参数预先传给 console.log不再依赖闭包捕获变量。我当年看到这种题的时候就在想实际开发中到底有用吗答案是太有用了。比如你要给一组异步请求的结果做批量渲染或者在一个循环里给每个元素绑定事件处理器凡是遇到循环中创建异步回调的场景这个考点就会从笔试题变成线上 bug。我记得有一个真实案例同事在 for 循环里批量创建了上传请求回调里用了循环变量作为文件索引结果全部上传完成后取索引取到的全是最后一个。排查到的原因就一句话闭包捕获的是变量本身而不是变量当前的值。2.3 原型链构造函数、实例、原型对象的三角关系原型链的考题花样比较多但核心就一张三角关系图构造函数有 prototype 属性指向原型对象原型对象有 constructor 属性指回构造函数实例内部有一个__proto__现在推荐用 Object.getPrototypeOf指向原型对象。用友笔试里这类题容易出现的形式是function Person(name) { this.name name; } Person.prototype.sayName function() { console.log(this.name); }; var p new Person(Tom); console.log(p instanceof Person); // true console.log(p instanceof Object); // true console.log(p.constructor Person); // true console.log(Person.prototype.constructor Person); // true每一行输出你都能答对只能算及格。真正的加分项是你能不能当场画出来 p、Person、Person.prototype 三者之间的引用关系并且解释清楚instanceof的机制——它是沿着__proto__链逐级查找看左侧对象的原型链上有没有右侧构造函数的 prototype 对象。再进一步如果面试官在此基础上问你Person.prototype { name: default, sayName: function() { console.log(this.name); } }; var p2 new Person(Jerry); console.log(p2.constructor Person); // false console.log(p2.constructor Object); // true这就是笔试中一个比较深的考点当你直接给 Person.prototype 整体赋值一个新对象时这个新对象没有 constructor 属性了所以顺着原型链找到了 Object。很多框架源码里会做这样一行修正Person.prototype.constructor Person;为什么要这么干因为很多工具库、类型判断、组件实例化机制会依赖 constructor 属性来反向获取构造函数。你平时用 Vue 的时候感觉不到这回事但如果你自己写组件库或者用 mixin 方式扩展类这种细节就会直接影响代码正确性。3. 浏览器与事件机制从冒泡/捕获到事件委托的实战延伸3.1 事件传播的三个阶段与 addEventListener 的第三个参数前端笔试特别爱考事件模型。DOM 事件的完整传播路径包含三个阶段捕获阶段从 window 沿 DOM 树向下到目标元素、目标阶段到达目标元素、冒泡阶段从目标元素沿 DOM 树向上回到 window。div idouter div idinner点击我/div /divvar outer document.getElementById(outer); var inner document.getElementById(inner); outer.addEventListener(click, function() { console.log(outer 冒泡); }); inner.addEventListener(click, function() { console.log(inner 冒泡); }); outer.addEventListener(click, function() { console.log(outer 捕获); }, true); inner.addEventListener(click, function() { console.log(inner 捕获); }, true);如果把这段代码放到浏览器里点击 inner控制台的输出顺序是outer 捕获 → inner 捕获 → inner 冒泡 → outer 冒泡。注意在目标阶段事件监听器的触发顺序取决于注册顺序而不是捕获/冒泡标志。如果希望在事件到达目标元素时只触发一次可以用{ capture: true }加{ once: true }或者直接在函数内部调用stopPropagation。但这里有个隐藏的坑stopPropagation只是阻止事件继续传播并不会阻止同元素上其他监听器的执行如果你需要阻止后续监听器得用stopImmediatePropagation。这个知识点是 2017 年笔试里比较细节控的题目放到实际工作中它直接对应的是表单验证、动态组件事件隔离等场景。3.2 事件委托笔试常考但真正用好的人不多事件委托的原理是利用事件冒泡在父元素上统一监听事件通过判断 target 来决定执行哪个逻辑。笔试常考方式有两种一是问你下列代码ul上绑定的 click 事件如何得知点击的是哪个li二是让你实现一个通用的委托函数。var list document.getElementById(list); list.addEventListener(click, function(e) { var target e.target; while (target ! null target ! list) { if (target.tagName LI) { console.log(target.textContent); break; } target target.parentNode; } });这里用 while 循环是因为 e.target 可能不是li而是li内部的某个 span 或者 a 标签必须向上查找。另一种更简洁的做法是使用closest方法list.addEventListener(click, function(e) { var li e.target.closest(li); if (li list.contains(li)) { console.log(li.textContent); } });closest在 2017 年那会儿兼容性还没有现在这么好但现在已经是现代浏览器的标配了。闭卷笔试一般不会让你写closest因为考的是原理。但如果你在试卷上用了closest并解释清楚它和 while 循环的等价关系就能体现你对新 API 的掌握程度。事件委托的价值在于如果在一个列表上有 1000 个 li 每个都绑定事件处理器内存开销和初始化耗时都是不可忽视的用委托方案只需要在父元素上绑定一个处理器新增子节点也无需重新绑定。这在动态渲染列表、表格、菜单这类高频使用场景中非常普遍。Vue 的事件监听和 React 的合成事件系统本质上都借鉴了事件委托的思路——React 会在根容器上统一绑定事件再按照虚拟 DOM 的路径分发。所以这道题答得好不好代表的不只是你会不会用 addEventListener而是你有没有从浏览器机制的角度去理解主流框架的设计。3.3 事件循环基础题和压轴题的分水岭JavaScript 是单线程语言事件循环机制决定了异步代码的执行顺序。2017 年的笔试题已经出现 setTimeout、Promise、async/await 混合的题型了虽然那时候 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。这个结果背后的规则是Promise.then属于微任务会在当前宏任务结束、下一个宏任务开始之前执行setTimeout属于宏任务即使延迟时间设置为 0也要等到当前调用栈清空且微任务队列清空后才执行。所以setTimeout 0并不是立刻执行而是要排到所有微任务之后。这个考点对后续理解前端性能优化非常重要——如果你把一些关键逻辑放在微任务里它可以比 setTimeout 更早执行但如果在微任务里又产生了大量微任务就会造成长任务阻塞渲染。用友笔试如果考到这里通常还会附加追问process.nextTickNode 环境和Promise.then谁先执行答案是process.nextTick在 Node 里比微任务还要优先。但如果你是在浏览器环境开发一般你只需要掌握浏览器的事件循环就够用了。4. CSS 布局与兼容性从盒子模型到多方案居中的完整拆解4.1 盒子模型标准模型和 IE 模型的区别必须背到骨子里CSS 的盒子模型题目在笔试中几乎是送分题但也最容易丢分。标准盒子模型中元素占据的总宽度 contentcontent-box宽度 padding border而 IE 盒子模型box-sizing: border-box中元素指定的宽度已经包含了 padding 和 border。如果你写了一个宽度为 100px、padding 为 20px、border 为 5px 的标准盒子实际占用的宽度是 150px而换成 border-box元素的实际内容区只有 50px。用友作为面向企业服务的厂商对兼容性的关注度历来很高所以这道题背后藏着一个实际生产问题老项目里充满各种被 padding 撑爆的布局 bug。2017 年那会儿移动端适配流行REM border-box的方案核心原因就是 border-box 让布局计算更直观你定了 100px它就是 100px不管里面怎么加 padding。所以现在几乎所有组件库的全局样式都会加这么一句*, *::before, *::after { box-sizing: border-box; }这不算什么高深的技巧但如果你是 2017 年参加笔试的应届生能主动说出这句重置代码并解释为什么组件库普遍这么做这就是加分项。4.2 水平垂直居中的三种方案必须知道为什么笔试里居中题的另一大考法是给你一个父容器和子元素要求写出至少三种水平垂直居中方案并说明各自的适用场景。以下是我在项目里最常用的几种:方案一flex 布局.parent { display: flex; justify-content: center; align-items: center; }这是目前最推荐的方式代码简洁语义清晰。缺点是对老旧浏览器IE10 以下支持不好但 2017 年之后做企业级系统一般也只需要兼容 IE11。方案二绝对定位 transform.parent { position: relative; } .child { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }使用 transform 做偏移不会影响文档流也不需要知道子元素的宽高。但如果子元素内部有文本缩放或使用了某些特效transform 偶尔会带来渲染精度问题比如文字轻微模糊。它还有一种变体是利用 margin 负值但要求子元素宽高固定所以反而不太推荐。方案三grid 布局.parent { display: grid; place-items: center; }一行搞定2017 年笔试基本不会要求这个因为兼容性还不行但现在做内部系统或现代浏览器环境是完全没问题的。如果将来面试官问grid 和 flex 怎么选你就说 grid 擅长二维布局flex 擅长一维布局居中只是它们的子集能力。这类题目真正想考察的不是你能不能背出这三种方案而是你会不会根据场景做取舍。如果你回答一律用 flex说明你考虑得不够全面如果你能主动说明如果子元素宽度不定、高度由内容撑开transform 方案最稳如果有大量文本需要换行则推荐 flex text-align: center这才是加分答案。4.3 移动端适配与常见兼容性陷阱用友这类企业级应用2017 年已经有不少移动端 H5 项目了所以 CSS 里大概率会有一道移动端适配题。常见的考点是 viewport 设置meta nameviewport contentwidthdevice-width, initial-scale1.0 /以及在什么情况下会出问题如果 initial-scale 设置成小于 1 的值会导致排版视口变宽页面整体被缩小。很多团队的模板里会刻意地写成这样meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableno /禁止缩放确实在某些场景下能避免页面错乱但是对无障碍访问非常不友好所以现在的主流实践已经不再强制 user-scalableno 了。如果用友笔试考到这个 meta 字段你可以顺势说明 accessibility 的考量这会让面试官觉得你不是背答案而是真的在思考用户体验。兼容性陷阱里还有一个高频考点inline-block 元素之间的空白间隙。两个 span 或两个 li 如果中间有换行或空格渲染出来会有一个约 4px 的间隙解决办法包括去掉空格、给父容器设置 font-size: 0、使用 flex、或者用 margin 负值。笔试如果结合代码让你解释为什么两个 inline-block 布局的盒子中间多了一小段空白你一定要答出这是因为 HTML 源码中的空白字符被渲染成了空格而不是什么神秘的 bug。这个问题的实际发生频率远超你的想象——尤其是在 CMS 后台管理系统某个列表的按钮之间莫名多了间距排查半天发现是模板引擎渲染时在元素之间插入了换行符。5. ES6 语法进阶与编码题实战箭头函数、解构赋值、Promise 与数组算法5.1 箭头函数不是函数简写那么简单箭头函数在 2017 年的笔试题里已经是必考重点了它和普通函数的核心区别包括没有自己的 this箭头函数中的 this 是在定义时从外层词法作用域继承的。不能用作构造函数换句话说不能对它使用 new。没有 arguments 对象如果需要使用类数组参数得用 rest 参数。不能使用 yield所以不能作为生成器函数。常见考题有var obj { name: obj, getName: () { return this.name; } }; console.log(obj.getName()); // 输出什么输出结果取决于外层作用域如果这段代码直接运行在全局环境中严格模式下 this 是 undefined非严格下是 windowwindow.name 大概是空字符串。因为箭头函数的 this 跟 obj 没关系它是在定义时绑定了外层词法作用域。有不少人以为对象方法里用箭头函数就能让 this 指向对象这是完全的误解。实际开发中箭头函数最常见的应用场景就是事件回调和异步回调比如button.addEventListener(click, () { // 这里的 this 不会因为是 click 回调而改变 });但也有反例需要警惕如果动态绑定 this 是你的核心诉求比如在 Vue 的 methods 里使用箭头函数定义方法就会导致内部的 this 无法指向组件实例。这是 2017 年从 jQuery 转 Vue 的人最常犯的错误之一。5.2 解构赋值、扩展运算符与模板字符串笔试的默写题这些题目往往不会非常难但需要答得准、答得快。比如解构赋值交换两个变量[a, b] [b, a]注意不需要临时变量。扩展运算符复制数组与对象合并const arr2 [...arr1]、const merged { ...obj1, ...obj2 }。模板字符串中嵌入表达式hello ${name}。实际笔试中考解构赋值最常见的坑是变量声明的缺省值和函数参数默认值function foo({ x 10, y 20 } {}) { console.log(x, y); } foo(); // 10 20 foo({ x: 1 }); // 1 20如果函数参数没有默认值 {}直接调用foo()就会因为解构 undefined 而报错。这个细节可以作为笔试题的加分点很多应届生知道解构但不知道给参数整体一个默认空对象。在企业级应用里函数常常接收一个配置对象尤其是组件封装时function createWidget(options {})这种防御式写法非常常用。5.3 Promise 的实现层次从会用到会链式调用再到会手写Promise 几乎是所有前端笔试的必考点。用友的笔试题二如果只考到Promise 有几种状态状态是否可以逆转那属于基础题。状态有 pending、fulfilled、rejected 三种一旦从 pending 变到其他状态就不能再变了。但如果继续加强度就会变成这样一道题var p1 new Promise((resolve, reject) { setTimeout(() resolve(p1), 1000); }); var p2 new Promise((resolve, reject) { setTimeout(() resolve(p2), 500); }); Promise.all([p1, p2]).then((res) { console.log(res); // [p1, p2]等待所有 Promise 完成后才执行 });以及它的变体Promise.race([p1, p2]).then((res) { console.log(res); // p2因为 p2 先 resolve });Promise.all有一个重要特点只要有一个 Promise reject整体就 reject并且当有一个 reject 时它并不会取消其他 Promise 的执行。很多实际开发中的 bug 就来自这里——你用了 Promise.all 发起三个请求其中一个失败另外两个请求仍然在跑甚至可能产生副作用。如果你在笔试题里能主动提到失败时不建议继续等待其余请求这就是经验的价值。更进一步如果面试官问如何实现一个带并发限制的 Promise 调度器那就是在考察你是否理解异步控制流。这种题在我做过的校招简历里很少见能答完整的但如果能答出来基本上就是 offer 候选人了。5.4 数组去重与排序一道简单的题能有多少种解法前端笔试经常考数组去重因为它的解法特别多每种解法都能暴露你对 JavaScript 核心 API 的熟悉程度。常见解法有// 方法一Set function unique(arr) { return [...new Set(arr)]; } // 方法二filter indexOf function unique(arr) { return arr.filter((item, index) arr.indexOf(item) index); } // 方法三Map 标记 function unique(arr) { var map new Map(); return arr.filter((item) { if (!map.has(item)) { map.set(item, true); return true; } return false; }); }如果数组里有对象Set 和 indexOf 都不能按照对象的内容去重因为对象比较的是引用。如果有 NaNarr.indexOf(NaN)返回 -1因为 indexOf 内部使用严格相等NaN ! NaN。但 Set 可以正确去重 NaN。这两种细节都是面试追问的经典方向。再配合一道排序题比如手写快排function quickSort(arr) { if (arr.length 1) return arr; var pivotIndex Math.floor(arr.length / 2); var pivot arr.splice(pivotIndex, 1)[0]; var left []; var right []; for (var i 0; i arr.length; i) { if (arr[i] pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } return quickSort(left).concat([pivot], quickSort(right)); }快排平均时间复杂度 O(n log n)最坏情况 O(n^2)退化的原因在于每次选基准都选到了最大值或最小值导致左右两部分极不均衡。如果用友笔试让你手写排序再用几句话说明时间复杂度那基本就覆盖了一轮面试的算法考察了。6. 综合编程题的解题思路需求模糊、边界不清时你如何展示工程思维综合编程题才是这张卷子里最容易拉开差距的部分。它通常不会写得太明确而是像真实需求一样给你一段描述让你实现某个组件或功能可能是一个 Tab 切换、一个倒计时、一个简单的事件总线或者是一个数据格式化函数。描述里没有给好完整的 API 设计没有明确边界条件甚至没有告诉你是否需要考虑性能。这种题考察的不是你会不会写代码而是你拿到一个需求之后脑子里先想什么。正确的做题顺序应该是先明确输入输出。用什么数据结构进来期望什么结果出去。再列出边界条件。空数组怎么办类型不对怎么办参数缺省怎么办然后设计核心逻辑。先写主流程再加防御。最后写测试用例。哪怕笔试没有要求测试你自己心里也要过一遍常规输入、边界输入、异常输入三组用例。我举一个典型的例子实现一个flatten函数把嵌套数组展开为单层数组。简单的写法是递归function flatten(arr) { var result []; for (var i 0; i arr.length; i) { if (Array.isArray(arr[i])) { result result.concat(flatten(arr[i])); } else { result.push(arr[i]); } } return result; }这个能跑但效率有问题。每一个递归层都创建新数组、做 concat数据量大的时候开销不小。你可以在笔试里用reduce和扩展运算符改进function flatten(arr) { return arr.reduce((prev, next) { return prev.concat(Array.isArray(next) ? flatten(next) : next); }, []); }但面试官更想看到的可能是你具备先拍平一层再持续拍平的思路或者直接使用arr.flat(Infinity)——虽然 ES2019 才正式确认但 2017 年已经在草案中了你能写出来说明你关注新特性同时你会解释浏览器兼容性如果不支持我再退回到递归方案这种策略性思维比功能本身更值钱。再举个例子实现一个防抖函数。很多人能写出基础版function debounce(fn, wait) { var timer null; return function() { var context this; var args arguments; clearTimeout(timer); timer setTimeout(() { fn.apply(context, args); }, wait); }; }这一步能写出的人很多真正分出高下的是下面的问题如果希望第一次点击立即执行后续点击才做防抖该怎么处理这就要给 debounce 加一个immediate参数function debounce(fn, wait, immediate) { var timer null; return function() { var context this; var args arguments; if (immediate !timer) { fn.apply(context, args); } clearTimeout(timer); timer setTimeout(() { timer null; }, wait); }; }这段代码背后的逻辑是在第一次触发且 timer 为空时立即执行然后设置 timer在 wait 时间内后续触发都会不断重置 timer直到 timer 超时被置空下一次触发才会再次立即执行。这类题目不仅考察代码量更考察是否理解防抖和节流的本质区别以及immediate 参数带来的行为变化。如果你能在试卷的注释里写下immediate 模式用于提交按钮防止双击重复提交这个注释就能让面试官一眼看出你有真实项目经验因为只有真正被重复提交 bug 坑过的人才会关注这个参数。综合编程题的另一个方向是用原生 JS 实现一个小功能组件比如实现一个 Tab 切换。这道题考察的不仅是 JS 逻辑还包含 DOM 操作、事件绑定、样式管理。更合理的是用数据驱动的思路来写var tabs [ { id: tab1, title: Tab 1, content: Content 1 }, { id: tab2, title: Tab 2, content: Content 2 }, { id: tab3, title: Tab 3, content: Content 3 } ]; function render() { var container document.getElementById(app); container.innerHTML div classtabs ${tabs.map((tab, index) button>function formatDate(date) { var y date.getFullYear(); var m date.getMonth() 1; var d date.getDate(); return y - (m 10 ? 0 m : m) - (d 10 ? 0 d : d); }这个代码功能没问题但如果加上参数校验再改名成padZero辅助函数可读性会明显提升function padZero(num) { return num 10 ? 0 num : num; } function formatDate(date) { if (!(date instanceof Date)) { throw new TypeError(Expected a Date object); } var y date.getFullYear(); var m padZero(date.getMonth() 1); var d padZero(date.getDate()); return y - m - d; }这种防御式编程风格在企业项目中非常被看重因为企业的代码不是一次性写给自己看的而是要多人长期维护的。用友笔试题中往往有意设置一些看起来能跑但有隐患的题比如让你读取对象深层属性如果你直接写obj.a.b.c前置判断一律不做笔试就会丢分。正确的做法是function get(obj, keyPath, defaultValue) { var keys keyPath.split(.); var result obj; for (var i 0; i keys.length; i) { if (result null) return defaultValue; result result[keys[i]]; } return result ! undefined ? result : defaultValue; }这样的函数虽然简单但考察的是你有没有真实处理过接口返回结构不稳定的问题。在企业应用里第三方接口返回的数据经常缺字段你需要一个安全的取值函数来避免整页白屏。2017 年那会儿 lodash 的_.get还没有像现在这样成为大家心目中的标配如果你能在笔试中自己实现一个就已经领先绝大部分应试者了。调试能力的考察往往会藏在异常处理的题目里。如果你在代码里写了console.log来排查问题并且最终把日志留在代码里没删面试官通常会扣代码整洁分。如果在笔试中确实需要调试建议先用注释标明 TODO: 调试完毕后移除这展示的是你对代码质量的自我要求而不只是能做题。8. 从这一份笔试题反推的复习框架给准备校招和自查基础的人如果你现在正在准备校招或者想评估自己的基础功是否扎实建议把这份用友笔试题涉及的考点整理成一张清单按优先级排列模块考点建议掌握程度JavaScript 基础数据类型、类型转换、深拷贝/浅拷贝熟练、能写出手写实现作用域与闭包this 绑定、闭包陷阱、变量提升能解释原理并说出实践场景原型与继承构造函数、原型链、class 继承能画图、能手写组合式继承异步编程事件循环、Promise、async/await能讲清执行顺序、能手写基础 Promise浏览器渲染机制、重排重绘、事件模型能分析性能瓶颈、能处理事件委托CSS盒模型、定位、Flex、响应式能给出多方案并说明取舍ES6let/const、解构、模板字符串、模块化熟练使用并解释编译结果综合能力防抖节流、数组去重、数据格式化能手写、能讲清边界条件复习建议给三条第一条不要只刷题。把每一个知识点都对应到你在真实项目里用到的场景比如闭包对应模块封装和私有变量原型链对应组件基类扩展事件循环对应页面性能优化。如果做不到这一点你背十套笔试题也只能应付笔试过不了后面的面试追问。第二条多写手写实现。bind、call、apply、new、Promise、防抖、节流、数组去重、深拷贝、发布订阅这些经典实现题在笔试卷里出现的概率极高。每次手写时注意边界条件函数参数要不要考虑长度this 要不要穿透目标环境是浏览器还是 Node这些细节才是拉开分数的关键。第三条练表达像讲课一样把知识点讲给别人听。前端笔试虽然写在纸面但真正决定面试结果的往往是你能不能把解题思路完整地说出来。你在复习时可以给自己出一道题请解释setTimeout和requestAnimationFrame在动画场景下的区别请说明position: absolute和position: fixed的定位参考点有什么不同请描述从输入 URL 到页面渲染完成的完整过程。这些题目看起来是背诵题但表达者的逻辑深度面试官一听就知道。用友 2017 年校招 web 前端笔试题二放在今天看题目本身或许有些旧了但它背后考察的能力模型并没有过时基础是否扎实、理解是否深入、工程意识是否到位。把这份卷子当成一面镜子仔细照一照自己比刷十套新题更有价值。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表