news 2026/8/29 22:13:07

58同城2017前端笔试题全复盘:从JS基础到工程化核心考点

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
58同城2017前端笔试题全复盘:从JS基础到工程化核心考点

每年一到秋招季,就有不少人翻出58同城2017年的前端笔试卷来对照复习。说实话,这张卷子放在今天看,知识点依然不过时,甚至很多题目现在面试还在变着法考。我当时刷这张卷子的时候踩了不少坑,有的题第一眼觉得简单,结果一写就漏洞百出;有的题当时完全没思路,后来回头补基础才发现考的全是硬功夫。

这篇文章就把这张卷子完整复盘一遍。我不打算只把题目和答案罗列出来,而是结合2017年前后端分离逐渐成熟、框架刚普及的行业背景,把每类题背后的考察意图拆开讲清楚,再给出完整的解题思路和参考答案。不管你是准备校招、社招跳槽,还是刚入行想系统补一遍基础,这份复盘应该都能帮你在复习时少走弯路。

1. 笔试全貌:2017年前端笔试在考什么

1.1 那个年代的前端生态与考察风向

2017年前端这个岗位处在一个很特殊的节点。jQuery还在大量老项目里跑着,但React已经火了两三年,Vue 2.0也发布了,前端工程化开始成为团队标配。与此同时,HTML5、CSS3、ES6这些基础能力成了用人单位的硬性筛选条件。笔试命题人的思路非常统一:既要筛掉只懂切图和套模板的选手,也要提前看看候选人有没有接触过模块化、组件化的开发方式。

所以你会发现,58同城这张卷子几乎覆盖了“基础能力、框架认知、开发素养”三个层面。基础题集中在JS语法、DOM操作、CSS布局和浏览器原理上;框架题不是考源码,而是考“你为什么用它、它解决了什么问题”;最后还有几道开放性的设计题,考察代码组织能力和问题分析能力。

我在准备时最大的感受是,这套题很“实”。没有偏题怪题,每一道都能在工作中找到对应场景。比如考事件冒泡,本质上是在考察事件委托;考闭包,本质上是在考察模块化封装的能力。命题人不是想难倒你,而是想通过笔试题判断“这个人能不能干活”。

1.2 试卷结构还原与各模块分值逻辑

结合多个渠道能找到的面试回忆和真题整理,58同城2017秋招前端笔试卷大致包含以下几类题型:

题型题量与分值占比考察点
单选与多选约15-20题,占比30%基础语法、CSS属性、HTTP状态码、DOM API
填空题约5-8题,占比10%输出结果、代码补全、概念填空
简答题约4-6题,占比25%原理说明、方案对比、场景分析
手写代码题约3-5题,占比35%算法、工具函数、组件实现

从分值逻辑就能看出,手写代码题是拉开差距的关键。选择题和填空题更多是“热身”,防止有人交白卷;而真正决定是否进入面试的,是你在手写题里展现出的编码习惯和思维深度。

我当时给自己定的目标是:选择题和填空题尽量不丢分,简答题答到点子上,手写题至少完整做出三道。按照这个目标去分配复习时间,效果比漫无目的刷题好很多。

2. 核心考点逐个拆解:JS基础与手写题

2.1 作用域、闭包与this指向

这一块几乎是所有前端笔试的“钉子户”。58这张卷子里有一道很经典的题,考察的是var声明与闭包的组合。原题大概是这样的:循环里用var声明变量,给按钮绑定点击事件,问点击后输出什么。

<button>按钮1</button> <button>按钮2</button> <button>按钮3</button> <script> var buttons = document.querySelectorAll('button'); for (var i = 0; i < buttons.length; i++) { buttons[i].onclick = function() { console.log(i); }; } </script>

答案是连续输出3。原因在于var声明的i是函数级作用域,循环结束后i已经变成3,而事件回调里引用的i是同一个变量。考察的目的不是让你背答案,而是看你能不能讲清楚“为什么”。我当时把三种解决方案都写了:用let块级作用域、用闭包保存每次循环的i、用事件对象的dataset属性。笔试时时间充裕的话,建议把多种解法都写上去,阅卷人会认为你的思路比较开阔。

this指向的题同样高频。经典的组合是:普通函数调用、对象方法调用、call/apply/bind调用、箭头函数调用,四种情况分别输出什么。这里有个不太容易说清的点是箭头函数:它的this不是在调用时确定的,而是在定义时从外层作用域继承的。我曾经在笔试里把箭头函数的this误判成“指向window”,后来复盘时才发现,关键在于它没有自己的this绑定。

2.2 事件循环、setTimeout与Promise的执行顺序

事件循环这道题,放在2017年其实已经开始流行了,因为Promise已经普及,异步编程从回调时代跨入Promise时代。58这张卷子里的考察方式是给一段混合了setTimeout、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。核心逻辑是微任务(microtask)优先于宏任务(macrotask)执行,Promise的then属于微任务,setTimeout属于宏任务。

我当时栽过跟头,原因是把“setTimeout 0毫秒”理解成了“立即执行”。实际上,0毫秒只是表示延迟时间的下限,浏览器最快也要到当前任务执行完后才去处理它。理解这一点之后,这类题基本就能做对了。

2.3 原型链与继承手写

原型链是2017年笔试的必考内容,那时候ES6的class虽然已经出现,但笔试题还是倾向于让你用ES5实现继承。原因很简单:ES5的继承能看出你对函数、原型、构造函数的理解是否透彻。

比较有代表性的一道题是:实现一个寄生组合式继承,并说明它相比其他继承方式的优势。标准的实现思路是:

function Parent(name) { this.name = name; this.colors = ['red', 'blue']; } Parent.prototype.getName = function() { return this.name; }; function Child(name, age) { Parent.call(this, name); this.age = age; } Child.prototype = Object.create(Parent.prototype); Child.prototype.constructor = Child; Child.prototype.getAge = function() { return this.age; };

关键点有两个。第一,通过Parent.call在子构造函数里调用父构造函数,把父类实例属性继承过来,避免了共享引用类型属性带来的问题。第二,通过Object.create(Parent.prototype)把子类原型指向父类原型的副本,而不是直接赋值为Parent.prototype,这样给Child.prototype添加方法时不会污染Parent.prototype。

笔试现场经常有人把Child.prototype.constructor = Child这一行漏掉。虽然很多场景下不影响功能,但严格来说,constructor指向不正确会导致instanceof判断异常,也会让阅读代码的人觉得你基础不扎实。

3. 浏览器与CSS布局:笔试里的“送分题”与“陷阱题”

3.1 CSS盒子模型、BFC与居中方案

CSS题在笔试里看着简单,实际上特别容易丢分。58这张卷子对CSS的考察集中在几个高频话题上:标准盒子模型与IE盒子模型的区别、BFC是什么以及如何触发、垂直水平居中有哪些方案。

盒子模型这块,我在复习时给自己画了一张对照表,笔试前过一遍非常有用:

对比项标准盒模型(content-box)IE盒模型(border-box)
width含义只包含content包含content、padding、border
最终元素宽度width + padding + border就是width本身
设置方式box-sizing: content-boxbox-sizing: border-box
适用场景老代码默认行为移动端布局、栅格系统,更方便

BFC(块级格式化上下文)的考察形式通常是:问BFC是什么,以及哪个属性可以触发BFC。常见触发方式有:float不为none、position为absolute或fixed、display为inline-block或flex、overflow不为visible。问到BFC能解决什么问题,我一般会举三个例子:清除浮动、防止margin塌陷、阻止元素被浮动元素覆盖。

居中方案这道题必须有条理地列举。水平居中可以用margin: 0 auto、text-align: center、flex的justify-content;垂直居中可以用line-height、flex的align-items、绝对定位加transform。笔试阅卷人比较看重的是你知道每种方案的适用场景,而不是单纯背代码。

3.2 经典布局题:两栏、三栏与圣杯布局

2017年笔试特别喜欢考布局手写,58这张卷子也不例外。常见考题是:左侧固定宽度,右侧自适应,要求写出多种实现方案。

最常见的两种写法是float+margin和flex。float方案的代码结构是:

<div class="left">固定宽度200px</div> <div class="right">自适应</div>
.left { float: left; width: 200px; } .right { margin-left: 200px; }

flex方案更简单,父容器开启flex,左侧给定宽度,右侧flex: 1。这里有一个细节经常被忽视:用float布局时,right的margin-left必须大于等于left的宽度,否则会出现重叠;而用flex布局时,不需要考虑这个问题。

三栏布局的完整做法一般要求写圣杯布局或双飞翼布局。我在笔试中更倾向用flex实现,因为代码更短、逻辑更清晰。但复习时还是要把圣杯布局的原理搞清楚,因为有些面试官会问“为什么中间列要写在最前面”这类问题,这涉及到HTML解析顺序和对SEO的考虑。

3.3 HTTP缓存与跨域:为什么笔试必考

HTTP缓存和跨域几乎是每年笔试的压轴常识题。2017年,前后端分离开始流行,前端工程师要自己处理接口联调和缓存策略,这两个知识点直接关系到线上性能和数据安全,所以必考。

HTTP缓存的考察形式通常是:强缓存与协商缓存的区别、相关响应头有哪些、用户刷新页面时缓存策略如何变化。我比较习惯用一张流程表来理清思路:

场景强缓存命中协商缓存命中缓存失效
请求头无网络请求发送请求,带上If-Modified-Since或If-None-Match重新下载资源
响应状态200(from disk cache)304 Not Modified200 OK
相关头Expires / Cache-ControlLast-Modified / ETag

跨域题考察的则是:什么是同源策略,跨域有哪些解决方案。JSONP、CORS、postMessage、代理转发,每种都要能说出一两句原理。我在笔试中遇到的一道开放题是“如果接口跨域了,你怎么排查和解决”,考的不只是知识点,还有排错思路。

4. 框架与工程化初探:2017年的加分题

4.1 数据双向绑定与虚拟DOM

2017年的笔试卷里,框架题基本不会让你手写React或Vue的源码,而是考察你对核心机制的理解。58这张卷子有简答题,问Vue的双向数据绑定原理,以及React的虚拟DOM为什么能提升性能。

Vue 2.x的双向绑定原理,标准回答是:通过Object.defineProperty重写数据的getter和setter,在getter中收集依赖,在setter中触发更新,配合观察者模式完成视图更新。需要特别说明的是,这种实现方式有局限性,比如数组的索引变化和对象新增属性无法被检测到,所以Vue才提供了$set方法。

虚拟DOM的考察角度是性能优化。我在回答这类题时会强调:虚拟DOM不一定比直接操作DOM快,它真正解决的问题是“把多次DOM操作合并成一次”,以及“通过diff算法最小化DOM操作范围”。这个说法在2017年算比较前沿的观点,放到今天依然成立。

4.2 构建工具的基础认知

2017年的前端笔试已经开始涉及构建工具,但很少考具体配置,更多是让你解释webpack的核心概念。我在复习时总结了一套“一句话理解法”:entry是入口文件,output是打包输出位置,loader让webpack能处理非JS文件,plugin则用来做打包之外的各种事情。

58这张卷子里有一道填空题,问loader和plugin的区别。不少人在这个地方失分,因为两者的边界容易模糊。我的记忆方法是:loader是“翻译官”,把scss编译成css、把ES6转成ES5;plugin是“管家”,负责代码压缩、热更新、环境变量注入这些杂活。一个管内容转换,一个管流程扩展。

4.3 手写发布订阅

框架题的另一种考法是让你实现一个发布订阅模式,因为Vue的$on、$emit就是基于这个模式。手写这个类在笔试里是个性价比很高的题,代码不长,但能考察你的事件管理能力。

我当时的参考实现是这样:

class EventEmitter { constructor() { this.events = {}; } on(event, callback) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(callback); return this; } emit(event, ...args) { const cbs = this.events[event]; if (cbs) { cbs.forEach(cb => cb(...args)); } return this; } off(event, callback) { if (!callback) { delete this.events[event]; return this; } const cbs = this.events[event]; if (cbs) { this.events[event] = cbs.filter(cb => cb !== callback); } return this; } }

笔试时如果能顺便写上once(只触发一次)的方法,会是一个明显的加分项。现在不少面试题就是从这些笔试题演变而来的,实现思路没变,只是要求提高了。

5. 笔试题里的代码实战:典型题完整解答

5.1 数组去重与排序

手写代码题里,数组去重可以说是出镜率最高的题了。2017年的时候,ES6的Set已经被接受,但笔试题仍然会要求你用ES5的方式实现,考察你对indexOf、对象属性名、双重循环等方案的掌握程度。

几种常见实现的对比:

方案思路优点缺点
双重循环遍历数组,逐个比较删除重复项兼容性好时间复杂度过高
indexOf用新数组判断是否存在代码简单无法区分NaN
对象键值对用对象属性做标记速度较快需处理对象键名冲突
SetArray.from(new Set(arr))最简洁环境需支持ES6

如果笔试要求写“通用去重”,我更推荐对象键值对方案加一层类型判断。要注意的是,直接用对象做标记时,数字1和字符串'1'会被当成同一个键,所以很多答案会用typeof加上value拼接成标记,避免类型混淆。

排序题通常是让你手写快速排序或归并排序。笔试现场写快排最容易出错的点有两个:一是递归的退出条件没写对,二是基准值选择方式导致性能退化。我习惯的写法是:

function quickSort(arr) { if (arr.length <= 1) return arr; const pivot = arr[0]; const left = []; const right = []; for (let i = 1; i < arr.length; i++) { if (arr[i] < pivot) { left.push(arr[i]); } else { right.push(arr[i]); } } return [...quickSort(left), pivot, ...quickSort(right)]; }

虽然这种写法不是最优的原地快排,但在笔试场景下,它最容易保证正确性。我当时的想法是先保证能跑通,再在注释里补充优化思路。

5.2 防抖与节流

防抖和节流在2017年已经属于“必会工具函数”级别了。58这张卷子里的考题是:解释防抖和节流的区别,并手写实现。

防抖的核心思想是“在事件触发后等待一段时间再执行,如果等待期间又触发了,就重新计时”。典型的应用场景是搜索框输入。节流的核心思想是“单位时间内只执行一次”,典型场景是滚动事件。

我的防抖实现:

function debounce(fn, delay) { let timer = null; return function(...args) { if (timer) clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, args); }, delay); }; }

节流实现:

function throttle(fn, interval) { let last = 0; return function(...args) { const now = Date.now(); if (now - last >= interval) { last = now; fn.apply(this, args); } }; }

有一个细节在笔试现场容易被忽略:定时器版的节流和防抖都涉及this指向问题。如果直接写setTimeout(() => fn(...args), delay),在严格模式下this就是undefined。我当时意识到了这一点,所以在实现里特意用了apply来保留this,这一点在很多参考答案里都没有体现。

5.3 深浅拷贝

深拷贝和浅拷贝的考察频率同样很高。主要原因是Vue和React中数据不可变性的概念越来越重要,如果不理解拷贝语义,就很容易写出一堆隐晦的bug。

浅拷贝常用方式有Object.assign、扩展运算符、Array.prototype.slice。它们的共同点是只拷贝第一层属性,嵌套对象仍然是同一个引用。

深拷贝的实现,笔试题一般不允许你直接写JSON.parse(JSON.stringify(obj)),因为这种方法无法处理函数、undefined、Symbol、循环引用。我当时的写法是递归实现:

function deepClone(obj, hash = new WeakMap()) { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags); if (hash.has(obj)) return hash.get(obj); const result = Array.isArray(obj) ? [] : {}; hash.set(obj, result); Reflect.ownKeys(obj).forEach(key => { result[key] = deepClone(obj[key], hash); }); return result; }

这个版本使用WeakMap处理循环引用,是比较完善的答案。笔试时如果时间紧张,至少要把递归拷贝和循环引用的问题答出来,会让阅卷人对你的水平评价上一个台阶。

6. 现场答题策略与时间分配

6.1 拿到卷子先做三件事

笔试和面试不一样,面试拼的是临场反应,笔试拼的是时间管理和查漏补缺的能力。我当时拿到卷子后不会立刻动笔,而是先花三分钟做三件事:扫一遍所有题目,标记出确定能做对的题、有思路但需要时间的题、完全没头绪的题;看一眼大题的分值比例,确定手写题要留多少时间;把试卷上出现的考点关键词在脑海里过一遍,相当于给自己做一个“知识激活”。

这里有个实用的小经验:简答题尽量写在最前面或者最后面,不要插在手写题中间。我踩过一次坑,原因是先做了手写题,耗费了大量精力,等到写简答题时大脑已经很疲惫,一些本来背得很熟的知识点反而组织不清楚了。后来调整了顺序,先解决记忆性的简答题,再集中精力攻手写题,效率明显更高。

6.2 每类题的时间红线

按照60分钟的笔试时长来算,我给自己划定过一条时间线:选择题和填空题控制在15分钟以内,简答题控制在20分钟以内,剩下25分钟全部给手写题。

选择题和填空题的“时间红线”尤其重要,因为这类题单个分值不高,一旦在某一道题上纠结太久,就会挤占后面大题的发挥时间。我的原则是:单选题超过1分钟没思路,先凭第一印象选一个,在题号旁边做个标记,等所有会做的题做完后再回来思考。

手写题的时间分配可以按照“先易后难”的顺序。一般来说,数组去重、防抖节流、简单继承是相对容易拿分的题,发布订阅和深拷贝难度略高。先拿稳容易的分,再挑战难题,心态上也不会崩。

6.3 草稿与卷面习惯

笔试的卷面习惯看似不重要,实际上很影响阅卷人的印象分。我在复盘时发现,字迹工整、步骤清晰的答案,即使不够完整,也更容易拿到步骤分。

写代码题时,我习惯先在草稿纸上写一遍,确认没有明显语法错误后再誊写到答题卡上。草稿阶段要注意变量命名和缩进统一。很多人在笔试现场一紧张,代码缩进就会乱掉,阅卷人很难读下去。另外,如果手写代码的空间不够,可以在题目旁边用箭头加注释,但务必保证标注清楚。

7. 复盘清单:我做错的那些题与反思

7.1 高频失分点

我在刷58这张卷子以及同类笔试卷时,整理出一份高频失分点清单,每一次对着它检查都能发现自己忽视了某个细节。

失分点典型错误正确思路
var循环闭包以为输出0、1、2输出3,需要用let或闭包保存
事件循环顺序以为setTimeout先行微任务优先于宏任务
this指向箭头函数this误判箭头函数的this由外层作用域决定
盒模型width理解错误区分content-box与border-box
深拷贝循环引用导致栈溢出用WeakMap记录已克隆对象
跨域方案只答JSONP至少补充CORS和代理方案

每次复盘时,我会把错题对应的知识点在笔记里单独标记一遍,复习时优先看这些标记。原因是笔试卷覆盖的知识点其实有限,真正让你丢分的往往就是那几个薄弱点,针对性补比盲目刷题效率高得多。

7.2 如何利用一张旧卷子构建复习体系

一张2017年的笔试卷,放到今天看内容已经不完全覆盖前端知识体系了,但它的复习价值在于帮你搭建“笔试答题框架”。

我的用法是:把卷子里的每道题归入“基础语法、浏览器原理、CSS布局、框架理解、工程化、算法能力、场景分析”这七个维度,然后针对每个维度补充10到20道同类题目。比如卷子里考了事件循环,我就把微任务、宏任务、async/await的各种组合都刷一遍;考了圣杯布局,我就把flex、grid、float三种布局方式全部重写一遍。

还有一个很关键的动作:把笔试题转成面试题。卷子里问“什么是BFC”,我就会追问自己“BFC在实际项目里解决过什么问题”;卷子里让写防抖,我就继续思考“如果需要在首次触发时立即执行,应该怎么改”。这种思维转换能让你从“会刷题”升级成“会做事”,到面试环节会非常占优势。

根据我个人经验,准备笔试最忌讳的就是只看不写。2017年备考时,我一度觉得自己知识点都会了,结果一上手写代码就漏洞百出。后来把每一道手写题都当作面试现场来练,规定时间、手写草稿、当场检查,半个多月后写代码的速度和准确度都提升了一大截。如果你现在也在准备前端岗位的笔试,不妨把这份卷子里的题目找出来,在纸上老老实实写一遍,你的体感会告诉你哪些地方还需要补。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 22:12:34

Agent Skills从概念到实战:构建可复用的AI能力包

各位读者朋友&#xff0c;大家好。最近 AI Agent 相关的概念越来越密集&#xff0c;尤其是 “Agent Skills”&#xff0c;这个词在技术社区和招聘 JD 里出现频率明显上升。但很多刚接触的朋友会困惑&#xff1a;它和 Tools、Function Call 有什么区别&#xff1f;是新的框架吗&…

作者头像 李华
网站建设 2026/8/29 22:11:11

从蓝桥杯国赛到嵌入式实战:单片机系统构建与工程思维迁移

1. 从赛场到工位&#xff1a;一次国赛经历带来的实战思维重塑 几年前&#xff0c;我带着一块开发板和一堆元器件&#xff0c;走进了第八届蓝桥杯单片机设计与开发大学组的国赛赛场。那几天高强度的烧脑、调试、排错&#xff0c;现在回想起来&#xff0c;早已不是几道具体的题目…

作者头像 李华
网站建设 2026/8/29 22:08:53

dcg快速上手教程:一行命令让Claude Code不再误删你的项目文件

dcg快速上手教程&#xff1a;一行命令让Claude Code不再误删你的项目文件 【免费下载链接】destructive_command_guard The Destructive Command Guard (dcg) is for blocking dangerous git and shell commands from being executed by agents. 项目地址: https://gitcode.c…

作者头像 李华
网站建设 2026/8/29 22:07:21

程序员中医之腰椎间盘突出的中医牵引与按摩

15-腰椎间盘突出的中医牵引与按摩 坐了一天&#xff0c;下班的时候腰疼得直不起来&#xff0c;有时候还带着一条腿麻、疼&#xff0c;从屁股一直疼到小腿&#xff0c;咳嗽、打喷嚏的时候疼得更厉害&#xff0c;晚上睡觉翻个身都疼。去医院拍个CT或MRI&#xff0c;报告上写着&qu…

作者头像 李华