ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

58同城校招前端笔试复盘:核心考点与备考策略

58同城校招前端笔试复盘:核心考点与备考策略 说实话看到“2020校招”“笔试”这两个关键词的时候我第一反应是有点恍惚。那时候前端校招的竞争已经非常激烈58同城算是国内招聘平台里技术面试比较有体系的一家他们的笔试筛人率不低题量、难度、范围都有一定的代表性。尤其是前端岗位考察的不是单纯的记忆型知识点而是看你有没有真正动手写过代码有没有把“会用”和“理解原理”区分开来。我整理这篇文章的初衷很简单把当年58同城前端笔试的考察逻辑、典型题目、以及我在备考和复盘过程中的体会完整地梳理出来。无论你现在是在准备校招还是想检验一下自己的前端基础是否扎实这份内容都可以当成一次模拟演练来用。我会尽量还原笔试的真实场景包括题型分布、时间节奏、难度感受以及那些“考试的时候觉得不难复盘时才发现‘我当时怎么没想到’”的细节。1. 笔试概况题型分布与考察范围1.1 当时的题型结构和整体节奏58同城2020届的校招笔试前端岗位的题目类型大致分为四块计算机基础选择题、JavaScript和浏览器概念题、编程/手写题以及一道综合应用类的简答题。整套试卷有一定的体量我记得当时在线笔试的时间大概是一个半小时到两个小时左右对着屏幕做题量不算小需要合理分配时间。这里有一个很重要的信息这类笔试不是单纯的“看你会不会”而是看你在规定时间内能不能稳定输出。很多人在校招笔试里挂掉不是因为题目多难而是因为节奏没把握好前面在选择题上磨太久后面的大题没时间写或者手写题只写了个大概思路没有补全细节。所以“限时训练”是备考阶段必须做的一件事不要觉得自己知识点会了就万事大吉。1.2 各模块占比和考察侧重点从占比来看JavaScript和浏览器相关的题目差不多占了一半以上。计算机基础的选择题相对比较简单主要是数据结构、操作系统、计算机网络里的高频考点比如数组和链表的区别、进程和线程的概念、TCP和UDP的区别。这些内容只要你大学里认真上过课或者专门刷过题库基本不会丢分。真正拉开差距的是编程题和手写题。这类题目考察的不只是“会不会实现”而是看你写的代码是不是符合前端的工程习惯。比如数组去重你写一个最简单的双循环可以但如果你知道怎么用Set、怎么用reduce实现还能说出各种方法的优缺点和适用场景得分就会有明显差异。面试官看的不是你写不写得出来而是你有没有深入思考过。1.3 线上笔试的环境与考场压力在线笔试还有个容易被忽略的点环境问题。我记得当时用的是牛客网的在线笔试系统你需要在浏览器里写代码没有本地的调试工具也没有代码提示。这个问题看上去不大实际上很影响发挥。建议你在备考阶段就多在类似的环境里练手比如牛客网、力扣的在线编程页面试试提交代码适应一下在文本框里写代码、没有自动补全的体验。另外一个细节是线上笔试的时候浏览器的其他标签页建议提前关掉以免不小心切出去造成不必要的麻烦。我当年还遇到过一次断网导致答案没提交上的情况虽然最后联系了HR重新安排但这种意外能避免就避免。考试前检查网络、设备、浏览器版本甚至要提前看一下摄像头权限和公平性检测这类流程别等到考前几分钟才手忙脚乱。2. 前端基础考点逐项拆解2.1 JavaScript核心闭包、原型链、this指向JavaScript这部分是笔试的绝对重点。58的卷子里出现了好几道跟闭包相关的题目其中有一道非常典型的“for循环里用var声明变量然后通过点击事件输出索引”的题目。这道题看起来简单实际上同时考察了作用域、闭包、事件循环和let/var的区别。for (var i 0; i 5; i) { setTimeout(function () { console.log(i); }, 1000); } // 输出结果5 5 5 5 5如果你只是背过“var没有块级作用域”那你可能知道答案但如果你能解释清楚“为什么是5而不是0 1 2 3 4”以及“改成let之后为什么就正常了”“如果不用let还可以用什么方式解决”那这道题才算真正吃透了。当时笔试里还问到了IIFE立即执行函数的解法也就是在循环体里包一层for (var i 0; i 5; i) { (function (j) { setTimeout(function () { console.log(j); }, 1000); })(i); }这里我要多说一句很多人在备考时喜欢背“let可以解决闭包问题”但笔试真正想考察的是你对作用域链和执行上下文的底层理解。let所产生的“每个迭代捕获独立绑定”的行为本质上是因为它每次循环都会创建一个新的词法环境而不是一个简单的“替代var”的语法糖。如果能在回答时主动把这个层次讲清楚一定会是加分项。原型链也是必考项。我当时遇到的题目大致是这样的定义了一个构造函数Person然后在Person.prototype上挂了一个方法接着用new创建实例判断实例、构造器、原型之间的关系。这类题目的核心是理清这么几条关系instance.__proto__ Constructor.prototype、Constructor.prototype.constructor Constructor、Constructor.__proto__ Function.prototype。如果你能自己画一遍原型链的图并且把“一个对象的原型是一个对象”“原型链的顶端是Object.prototype”这些链条理清楚基本不会出错。2.2 异步、事件循环与Promise事件循环和Promise是前端笔试里最常出现的“区分度题型”。58的笔试中有一道题是让写出以下代码的输出顺序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回调进入了微任务队列。学习事件循环时建议大家不要只记结论去理解一个核心模型JavaScript是单线程的同步代码执行完之后会先清空微任务队列再从宏任务队列里取出一个任务执行执行完再清空微任务队列如此循环。浏览器里的宏任务包括setTimeout、setInterval、I/O、UI渲染微任务包括Promise.then、MutationObserver、queueMicrotask等。理清这套机制之后碰到任何跟异步顺序有关的题目你都能推出来不需要死记硬背。2.3 深拷贝、防抖节流等手写题手写题是笔试里分值最大、也最考验代码功底的部分。58笔试里有一道手写深拷贝。大多数人都能写出第一版就是考虑一下基本类型和对象的递归但这道题真正的加分点在于你能不能让你的拷贝兼容数组、处理循环引用、考虑Symbol、函数等特殊类型的处理。我当时写了一个相对完整的版本核心思路是用WeakMap来存储已拷贝对象解决循环引用问题。下面是我参考类似的写法后整理的一个简化版本function deepClone(target, map new WeakMap()) { if (target null || typeof target ! object) { return target; } if (map.has(target)) { return map.get(target); } const cloneTarget Array.isArray(target) ? [] : {}; map.set(target, cloneTarget); for (const key in target) { if (Object.prototype.hasOwnProperty.call(target, key)) { cloneTarget[key] deepClone(target[key], map); } } return cloneTarget; }这里有一点很关键如果你只写了普通递归版本没有处理循环引用遇到一个自引用的对象比如obj.self obj就会栈溢出。补充WeakMap的思路不仅让代码更健壮也向面试官展示了你是真的理解深拷贝的痛点而不是背了模板。防抖和节流也是常见的送分题但很多人拿不到满分。我给出的建议是不只是默写代码你要理解使用场景。防抖是“触发后延迟执行如果在等待期内又触发了就重新计时”适合搜索框输入、窗口resize。节流是“固定时间间隔内只执行一次”适合滚动事件、下拉加载。我在笔试时先写了实现然后再标注适用场景让阅卷人看到你对边界条件的考虑。function debounce(fn, wait) { let timer null; return function (...args) { if (timer) clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); }, wait); }; } function throttle(fn, interval) { let last 0; return function (...args) { const now Date.now(); if (now - last interval) { last now; fn.apply(this, args); } }; }这里要注意throttle用时间戳实现第一次触发时会立即执行如果希望第一次不立即执行可以用定时器方案。把这些差异说清楚会让你的答案更完整。2.4 数组字符串操作与基础算法基础算法题在58笔试里以中等难度为主不会考太偏的动态规划或图论。比较常见的是数组去重、数组扁平化、字符串反转、斐波那契数列、求最大公约数等。这类题目虽然不难但讲究一个“能不能写出最优解并解释清楚”。以数组扁平化为例const arr [1, [2, [3, [4]]]]; // 方法一递归 function flatten(arr) { let result []; for (let i 0; i arr.length; i) { if (Array.isArray(arr[i])) { result result.concat(flatten(arr[i])); } else { result.push(arr[i]); } } return result; } // 方法二reduce简化 function flattenByReduce(arr) { return arr.reduce((acc, cur) { return acc.concat(Array.isArray(cur) ? flattenByReduce(cur) : cur); }, []); } // 方法三规整到指定深度 function flattenDepth(arr, depth 1) { return arr.reduce((acc, cur) { return acc.concat(depth 1 Array.isArray(cur) ? flattenDepth(cur, depth - 1) : cur); }, []); }这些问题说穿了就是考察你对递归、栈、数组方法concat、reduce、flat的掌握程度。刷题的时候建议给自己加一个要求每道题至少写两种解法并比较它们的复杂度。这样笔试中遇到真·附加题“你能用非递归实现吗”才不至于措手不及。3. 浏览器、网络与前端工程化3.1 浏览器渲染机制与缓存策略58的笔试对浏览器和网络相关的知识一点也不含糊。我记得选择题里有问浏览器从输入URL到页面加载完成的过程选项里混着DNS解析、TCP连接、HTTP请求、渲染引擎解析HTML/CSS/JS等步骤要求你排出正确顺序。这类“死记硬背”的题目其实很好拿分但你不能只背这一步还要理解渲染过程里的关键机制。比如为什么CSS放在head里JS放在body底部因为CSS会阻塞渲染而JS的下载和执行会阻塞DOM解析。如果把JS放在head里它会阻塞后续DOM的解析导致页面白屏时间变长。这个知识点我建议每个面试者都练习用“一段话完整说清楚”的方式包括以下几个层次渲染进程拿到HTML后开始解析DOM树遇到style标签或外链CSS时下载并解析CSS生成CSSOM树过程中如果遇到script标签会停止解析等待脚本下载并执行执行完毕后才继续解析剩余HTMLDOM树和CSSOM树合并成渲染树最后布局、绘制。缓存策略也是必考的。58的笔试题里出现了关于HTTP缓存的问题主要是强缓存和协商缓存。你需要知道Cache-Control和Expires的区别知道Last-Modified和ETag对应的请求响应头。我给大家一个很实用的梳理方式强缓存请求发出前浏览器直接判断本地缓存是否过期如果没过期就直接用不会发请求到服务器。相关字段Expires绝对时间、Cache-Control相对时间如max-age3600。协商缓存强缓存失效后浏览器带着校验字段去问服务器服务器告诉你用缓存还是返回新资源。相关字段Last-Modified/If-Modified-Since、ETag/If-None-Match。3.2 跨域原理与常见解决方案跨域问题在校招笔试里出现的频率非常高几乎每一场前端笔试都会遇到。58的题目里有一段关于JSONP的实现或者让你写出一个能够支持跨域请求的方案然后解释原理。JSONP的核心思想是利用script标签的src属性不受同源策略限制这个特性把请求放到script的src上服务端返回一段可执行的JavaScript代码从而把数据传给前端。这里有一个笔试高频考点JSONP和CORS有什么区别JSONP只能支持GET请求而且不是标准方案CORS是浏览器支持的标准机制需要服务端设置Access-Control-Allow-Origin等响应头。我当时在笔试里写跨域方案的时候除了JSONP和CORS还补充了“开发环境常用代理转发”和“postMessage实现跨窗口通信”两种。多写一两个方案不是坏事但前提是你能说清楚各个方案的适用场景。你写代理转发就要说明“这是开发调试期的方案生产环境还是要靠后端配置跨域头”你写postMessage就要提到iframe跨域和window.open之类的场景。3.3 前端安全基础XSS与CSRF安全相关的知识在工作后尤其重要但很多同学在校招时容易忽略觉得“这不是后端的事情吗”。实际上58的笔试里明确问到了XSS和CSRF的概念、危害和防御措施。XSS跨站脚本攻击的本质是攻击者往页面里注入恶意脚本。防御思路也很明确不要信任用户的任何输入对输出做转义。现在很多前端框架默认做了转义但如果你用了v-html、dangerouslySetInnerHTML或者innerHTML手动拼接HTML就存在被注入的风险。CSRF跨站请求伪造指的是攻击者诱导用户访问恶意网站利用用户已登录的身份信息向目标网站发起伪造的请求。防御手段常见的有校验Referer、使用CSRF Token、同源检测、双重Cookie验证等。考试时不需要你把每个方案都写得很细但至少要说清楚“核心原则让服务器能区分合法请求和伪造请求”。3.4 Webpack等工程化工具的基础用法前端工程化在笔试里主要以概念题为主不会让你在笔试系统里真的去配置Webpack。当时58考了关于loader和plugin的区别以及常用loader和plugin有哪些。loader是一个转换器做的事情比较单一就是把A文件转换成B文件比如babel-loader把ES6转成ES5css-loader处理CSS文件中的import和url()style-loader把CSS注入到DOM中。plugin是插件做的事情更广能参与到打包的各个生命周期比如HtmlWebpackPlugin生成HTML文件CleanWebpackPlugin清理输出目录。这两者的核心区别是loader处理的是模块转换plugin可以做任何webpack能力范围内的扩展。如果你时间充裕建议大家亲手配一次Webpack哪怕是一个简单的项目也行。因为笔试里如果问“如何优化Webpack构建速度”你没有实际配置经验就只能背一些关键词比如多进程打包thread-loader、缓存babel-loader的cacheDirectory、使用DLLPlugin预编译等。但如果你实际配过就能碰到类似“哦对还有配置externals来避免把第三方库打包进产物”这种细节面试时讲起来也会更有底气。4. 框架层知识的考察逻辑4.1 Vue和React到底考什么58校招笔试没有指定你必须用哪种框架但会考察你对主流框架的理解深度。题目相对灵活不是“背生命周期顺序”那种而是让你对比Vue和React的异同或者说“为什么Vue3用Proxy替代了Object.defineProperty”。以“Vue3为什么要用Proxy”为例这道题在网上已经被讨论烂了但笔试里能答好的同学并不多。核心答案有三点第一Object.defineProperty只能监听属性的读取和赋值对新增属性和删除属性无能为力Vue2只能通过Vue.set/Vue.delete这类API去补丁式处理第二Object.defineProperty需要递归遍历对象的所有属性对象层级一深性能开销就上来了第三Proxy可以直接代理整个对象无论是属性读取、写入、新增、删除还是数组索引和length变化都能拦截天然支持响应式。React这边的高频考点是“React组件的渲染机制”和“useEffect的依赖数组到底怎么工作”。笔试比较喜欢出一道代码题让你写出组件在不同生命周期阶段或者不同state变化下useEffect的执行顺序。这种题你只要把“render阶段和commit阶段”分开理解就能推出来useEffect的回调是在渲染完成后才异步执行的清理函数会在下一次渲染后执行之前被调用。4.2 算法与数据结构在笔试中的综合应用58笔试里的编程题虽然有前端特色但本质上还是考察算法和数据结构的功底。以我当时遇到的“实现一个带缓存的函数”为例它既和闭包相关又和哈希表对象/Map相关还涉及LRU缓存淘汰的思路。LRU缓存是一个在面试中非常经典的问题。核心思路是用一个Map来存储缓存项在插入和访问时保证最近的项在最前面当缓存容量超过上限时淘汰最久没访问的那一项。用JavaScript实现很简单class LRUCache { constructor(capacity) { this.capacity capacity; this.map new Map(); } get(key) { if (!this.map.has(key)) return -1; const value this.map.get(key); this.map.delete(key); this.map.set(key, value); return value; } put(key, value) { if (this.map.has(key)) { this.map.delete(key); } this.map.set(key, value); if (this.map.size this.capacity) { const oldestKey this.map.keys().next().value; this.map.delete(oldestKey); } } }笔试中如果你能把Map的迭代顺序插入顺序这个特性利用起来就比很多人写数组实现要高效。这种“把JavaScript语言特性融入到算法实现里”的能力恰恰是面试官最想看到的。4.3 综合应用题性能优化和网络安全58笔试的最后一道题通常是综合应用题不会只考一个点而是让你针对一个场景设计方案。我当时印象比较深的场景是“假设你负责一个移动端页面打开速度很慢你如何定位问题并给出优化方案”这种题没有标准答案但你的回答要有层次。可以从网络层面、资源加载层面、渲染层面、代码执行层面逐层展开。网络层面启用HTTPS和HTTP/2减少请求数合理配置缓存资源加载层面使用CDN图片使用合适的格式WebP、按需加载路由和组件代码分割渲染层面避免白屏可以使用loading态或骨架屏减少首屏JS体积关键CSS内联代码执行层面减少长任务对长列表进行虚拟滚动避免频繁操作DOM。这类题在笔试中很能体现工程经验。建议你平时做一些性能分析打开浏览器DevTools的Performance和Network面板记录几个指标比如首屏时间、DOMContentLoaded时间、资源总大小。当你对“慢”有真实的感知回答优化题时就不会只说空话。4.4 代码风格与可维护性58的笔试里简答题部分有一道和代码规范相关的题目比如“如何避免写了一堆‘屎山’代码”或者“前端代码如何做到可维护”。这类问题没标准答案但你要展现自己的工程意识。我觉得可以从几个方面来答一是模块化拆分组件和工具函数各自职责单一不复用的时候不要过早抽象二是命名清晰变量名、函数名要让下一个阅读代码的人能秒懂不要用a、b、c这种临时命名三是代码审查通过团队内的Code Review和ESLint等工具兜底保证代码风格统一四是写测试尤其是核心业务逻辑和工具函数。很多校招生在回答这类问题时很容易陷入“一定要用最新技术”“重构所有旧代码”的误区。实际上面试官更愿意听到的是“我会先补测试、再抽公共函数、再逐步替换”也就是你具备渐进式重构的意识而不是大破大立。5. 高频失分点与备考复盘5.1 笔试中常见的“隐形扣分项”结合58笔试和我后来帮别人复盘校招笔试的经验我发现失分点往往不在你不会的题上而在你会但没完全答对的题上。第一个隐形扣分项是手写题没有写“边界条件”。比如深拷贝很多人没考虑传入null或非对象的情况数组扁平化很多人没考虑空数组和传入非数组参数的情况。笔试系统不会像真实面试那样让你口述思路但阅卷人看得见你的代码是不是健壮。第二个隐形扣分项是“只写答案不写分析”。当笔试题目是“这段代码输出什么”时很多同学只写了输出结果没有解释过程。结果在人工阅卷阶段就不会给高分因为你没有展现出思考链条。建议养成一个习惯即使是选择题或填空题只要界面允许输入就把关键推导步骤写上去比如“因为Promise的then进入了微任务队列所以先于setTimeout输出”。第三个隐形扣分项是“编程题不测边界”。我见过很多代码写得不错的同学在数组边界、空值、undefined的取值上翻车。这里有一个技巧笔试时如果时间允许把你写的代码在脑子里跑一遍尤其是循环的起点和终点、递归的终止条件、可能出现的空指针。有时候一行if判断就能救回一道题。5.2 时间分配策略先做难题还是先做易题在线笔试没有监考老师提醒你时间所以自己心里要有个钟。我的策略是开考后先用5分钟扫一遍全卷标记出难题、常规题和送分题。第一次顺序先做送分的选择题和概念填空保证基础分全部入账然后做难度中等的简答题尽量多写要点最后集中精力攻手写题。手写题一定要放到后面的原因是容易卡住一旦卡住很容易消耗掉大量时间。如果一道编程题你10分钟还没有任何思路先跳过等最后再回来穷举或者写朴素解法。这种策略不是为了让你拿满分而是为了让你在有限时间内把该拿的分都拿住。另外笔试系统通常有“自动保存”功能但你在切换题目时还是要留意自己的答案是否保存成功。不要因为一个很小的系统操作失误丢了整道大题的分数。5.3 备考路线和资料推荐如果你是现在才开始准备前端校招我的建议是先做一次自我检测确定自己的基础在什么位置。如果JavaScript基础还比较薄弱先把《JavaScript高级程序设计》里的核心章节过一遍尤其是变量、作用域、闭包、原型链、异步、事件、DOM相关如果这些你已经比较熟悉直接进入刷题阶段。刷题资料的优先级我个人觉得是力扣的《JavaScript版数据结构与算法》热题 牛客网的“前端校招面试真题” 一些GitHub上的前端面试题库。刷题的时候要有意识地把题目分类比如“作用域和闭包一类”“事件循环一类”“手写API一类”这样比一天做十道不同的题效果更好。每一类你只需要吃透几道经典题然后举一反三。除了刷题强烈建议找一个“说题”的练习方式。你可以给自己录个音或者对着朋友、对着墙把一道题的解题思路和知识点讲出来。因为你笔试答题时本质上也是在“用书面语言把思路讲出来”。如果连说都说不清楚写出来大概率也是混乱的。5.4 复盘比刷题更重要最后想聊聊复盘。很多人刷题刷得很猛但做完就丢下次遇到类似的题目还是不会。我建议你建立一个自己的错题本不要直接抄题和答案而是记录“我当时为什么没做出来”“卡点在哪个知识点”“下次遇到可以怎么快速定位”。像58这套笔试题如果你做完复盘的时候把自己不会的每一道题都做一遍“这道题在考什么”的分析收获会大得多。我记得当时复盘“老生常谈的闭包循环输出”这道题时才真正地理解为什么ES6引入let是一个“规范层面”的改进而不是一个语法糖。这种从复盘里获得的“啊原来是这么回事”的瞬间越多你的基础就越扎实。应对笔试也好应对面试也好到最后拼的就是谁对基础理解的维度更高。这也是我从一次校招笔试里获得的最大价值。回到最开始说的58同城这场前端笔试难度说不上变态但它非常典型的映射了校招前端笔试的考察逻辑基础扎实、知识面广、代码能落地、思路能讲清楚。如果你能把这篇文章里的每一个考点都吃透不仅这场笔试你能hold住绝大多数公司的前端校招笔试也不会成为你的拦路虎。
返回列表