十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

京东2018秋招前端笔试真题拆解:从JavaScript原理到手写代码

京东2018秋招前端笔试真题拆解:从JavaScript原理到手写代码 京东的秋招笔试向来以“题量大、覆盖面广、手写代码要求高”著称2018届前端岗的这套题在当年的求职圈里被讨论了很久。和很多只考“背概念”的笔试不同这套卷子明显在往“理解原理 现场写码 边界条件处理”三个维度上使劲。无论你是准备面试的应届生还是想检验自己基础功底的从业者这套题都值得认真过一遍。我后来把网上能找到的回忆版、讨论帖以及和参加过笔试的同学交流的内容整合了一下从中提炼出了几个反复出现的核心考点这篇文章就从这些考点出发帮你把每一类题背后的逻辑讲透。1. 京东2018秋招前端笔试的真实面貌题型、时间与难度分布1.1 整张卷子的题型构成先说说这套卷子给我留下的整体印象。和阿里、腾讯那一类动辄四五个小时的大而全笔试不同京东2018秋招前端笔试的时间大概在100到120分钟之间题量却一点儿都不少。大致可以分为四块计算机基础与网络选择题、JavaScript与浏览器原理题、CSS布局问题以及最后两道压轴的手写算法题。选择题部分约20题覆盖HTTP状态码、TCP拥塞控制、浏览器缓存、同源策略、跨域方案偶尔也会出现数据结构里的栈、队列、二叉树遍历。这部分题看似零散实际上是在考察你大学期间“计算机基础”有没有形成体系。简答/分析题约4-6题这类题不给代码编辑器就是给你一段代码让你写出运行结果、分析输出顺序、指出内存泄漏的隐患或者让你优化某种写法。典型的比如执行上下文、事件循环输出顺序、闭包陷阱等。CSS与页面实现题约2-3题常见的是“实现一个两栏/三栏布局”“水平垂直居中”“BFC是什么如何触发”。看起来基础但越基础的题目越容易在细节上丢分。手写代码题约2题这一部分时间占比大现场在草稿纸上写代码。2018年前后端统一的面试题库里出现频率最高的是数组去重、深拷贝、节流防抖、Promise实现、事件总线等。京东的笔试题尤其喜欢考“实现一个函数要求考虑边界条件”而不是只让你写一个能跑通常规用例的版本。1.2 难度曲线与时间分配建议按照参加过的同学反馈这套卷子的难度并不是线性递增的而是“开头难、中间平稳、最后炸裂”。选择题里有一道关于HTTP/2头部压缩、TCP队头阻塞的题很多非科班同学第一次看到直接懵掉做到中间JavaScript原理题时心态开始恢复等到最后两道手写题一道是“实现一个带取消功能的防抖函数”另一道是“手写Promise.all”基本就在考验真功夫了。时间分配建议上如果你现在去模拟这套题我会建议选择题控制在30分钟以内遇到不会的不要纠结先蒙一个标记起来。JavaScript与浏览器原理题分配35分钟因为这类题往往需要你写分析过程不是选完就完了。CSS布局题控制在15分钟考查点集中关键是快速写出一个可行方案再补充说明优劣势。手写代码题留至少40分钟。这两题的代码量都不小而且很看重边界条件的处理比如传入空数组、传入非函数参数、取消状态切换等都需要考虑进去。这套题整体上传递了一个信号前端不是“会切图、会调接口”就行底层计算机基础、JavaScript语言机制、代码工程化意识缺一不可。我在下面几节里把几类高频题目逐一拆开来讲。2. 高频考点逐题拆解那些年我们绕不开的JavaScript与浏览器原理2.1 JavaScript核心机制从执行上下文到闭包先提出笔试里最常出现的一类题给一段代码让你说出输出顺序。这类题几乎必考而且京东尤其喜欢考“执行上下文、作用域链、闭包、var与let的循环绑定”混在一起的情况。比如网上流传度很高的这道变形题for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 100); }答案是连续输出5个5。原因在于var声明的i是函数作用域循环结束后i已经变成5五个定时器的回调共同引用了同一个i。如果改成let每次循环会创建一个新的词法环境i被绑定在当前迭代中输出就是0、1、2、3、4。很多同学以为背住“var是函数作用域、let是块级作用域”就够了但笔试真正考的是你对“执行上下文创建过程”的理解。var在上下文创建阶段就会被声明并初始化为undefined而let处于“暂时性死区”只有在执行到声明语句后才能访问。这也是另一道经典题——变量提升的由来console.log(a); var a 1;输出undefined而不是报错就是因为声明被提升到了当前作用域顶部但赋值还停留在原位置。如果你把var换成let控制台会直接抛出ReferenceError。京东这套笔试卷里闭包的考察方式也比较灵活不只是“闭包是什么”这种概念题而是给你一个真实场景比如实现一个计数器函数每次调用返回的值加一要求不能使用全局变量。这道题的标准思路是利用闭包保存状态function createCounter() { let count 0; return function() { return count; }; } const counter createCounter();真正拉开差距的地方在于追问如果两个地方同时调用createCounter()它们的状态是否共享答案是不共享因为每次执行createCounter都会创建新的函数作用域。这个理解如果不到位后面遇到节流防抖、函数柯里化、模块化封装都会出问题。2.2 浏览器渲染原理与性能优化题笔试选择题和简答题里浏览器相关的内容占了相当大的比重。高频考点包括“从输入URL到页面展示发生了什么”“浏览器缓存机制”“重绘与回流”“事件委托”。京东前端的题更偏向应用层比如页面滚动的性能优化有哪些方案为什么transform动画比修改left属性更流畅要答好这道题需要从“浏览器渲染管线”的角度说清楚。修改left属性会触发布局Layout阶段浏览器需要重新计算元素位置进而影响后续的绘制Paint和合成Composite而transform不会改变文档流浏览器可以直接跳过布局和绘制把元素交给合成器处理整个过程在GPU上进行。这就是为什么现在做动画都优先推荐使用transform和opacity。缓存机制相关题目是选择题里的必考题。重点要掌握强缓存Cache-Control和Expires命中后直接使用本地缓存不发请求。协商缓存Last-Modified/If-Modified-SinceETag/If-None-Match命中后返回304浏览器再读取本地缓存。优先级Cache-ControlExpiresETagLast-Modified。笔试中常见的坑是服务器返回的响应头里同时存在Expires和Cache-Control: max-age到底以哪个为准答案是以Cache-Control为准因为它出现得更晚对时间可控性更强。如果Cache-Control里没有max-age也没有no-cache只有no-store那就代表完全禁止缓存每次都要向服务器请求。这些细节在背概念的时候很容易忽略但出题人特别喜欢考。2.3 CSS与布局题看起来简单答全很难CSS题一般放在笔试中间你以为送分其实是最容易“自我感觉良好”的部分。以“水平垂直居中”为例多数人第一反应是flex但这道题如果只写一个方案通常只能拿一半分。完整的答题思路应该是方案一flex。父元素display: flex; justify-content: center; align-items: center;。方案二position transform。子元素绝对定位top: 50%; left: 50%; transform: translate(-50%, -50%);。方案三不知道宽高时用margin: auto配合绝对定位四个方向的偏移都设为0也能实现居中。方案四CSS Gridplace-items: center一行搞定但需要考虑兼容性。京东笔试里CSS布局题往往还会追问“如果子元素高度不固定哪种方案会出问题”。比如margin: auto的方案在旧版浏览器里至少需要子元素有宽度才能正常居中absolute margin负值的方案要求父元素有明确高度且子元素宽高已知。这些都答上才算真正掌握了这个知识点。BFC块级格式化上下文也是高频考点常以“如何解决外边距塌陷”“如何清除浮动”的形式出现。BFC触发条件包括浮动、绝对定位、display: inline-block、overflow非visible、display: flex等。理解了BFC之后很多CSS怪问题都能从机制层面解释清楚而不只是“加个overflow: hidden碰碰运气”。3. 出题人到底在考什么岗位能力模型与答题思路3.1 算法题之外的隐藏考察点很多人做这套题时会陷入一个误区拼命刷LeetCode结果发现笔试题里出现的算法题其实并不难难的恰恰是“把逻辑用JavaScript严谨地表达出来”。比如有一道广泛流传的京东真题实现一个函数flatten将嵌套数组扁平化要求不能使用Array.prototype.flat。思路其实很简单递归遍历数组遇到子数组就递归处理function flatten(arr) { const result []; for (let i 0; i arr.length; i) { if (Array.isArray(arr[i])) { result.push(...flatten(arr[i])); } else { result.push(arr[i]); } } return result; }但这道题在笔试中常见的丢分点有三个一是没有处理空数组的情况虽然递归可以天然跳过空数组但很多同学在写判断条件时写成了if (arr[i].length)导致空数组被当作值塞进去输出结果多出个空数组。二是用push(...flatten(...))展开一层后没有考虑超长数组的调用栈问题虽然这类题不会真的考到性能但如果你在注释里提到“这里用concat可以避免展开超大数组时的参数数量限制”面试官对你的评价会完全不同。三是只写了递归解法没有提迭代解法。其实flatten完全可以用栈来实现function flattenByStack(arr) { const stack [...arr]; const result []; while (stack.length) { const item stack.shift(); if (Array.isArray(item)) { stack.unshift(...item); } else { result.push(item); } } return result; }在笔试这种场景里能够写出两种解法并且分析各自的优劣比只写一种解法要加分得多。因为出题人想看到的不是“你会背这个函数”而是“你在真实业务里遇到嵌套数据结构时有没有属于自己的处理框架”。3.2 手写代码题的丢分点与满分思路再单独聊聊手写代码题这是京东这套笔试里区分度最高的部分。以“手写防抖函数”为例很多应届生都能写出核心逻辑function debounce(fn, delay) { let timer null; return function(...args) { clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); }, delay); }; }这道题如果只是这么写应该能拿到70%的分数但剩下的30%是在考边界条件。如果传入的不是函数应该直接抛错还是静默返回专业的前端库通常会抛出TypeError。fn内部的this应该如何绑定如果使用了箭头函数this会被错误绑定为定义时的上下文。首次是立即执行还是延迟执行有的业务需要immediate参数。是否需要提供cancel方法让外部能够手动取消防抖京东2018秋招笔试里流传比较广的一道题正是“实现一个带取消功能的防抖函数”。完整的做法是给返回的函数挂上cancel属性function debounce(fn, delay, immediate false) { let timer null; let isInvoked false; const debounced function(...args) { if (timer) clearTimeout(timer); if (immediate !isInvoked) { fn.apply(this, args); isInvoked true; } else { timer setTimeout(() { fn.apply(this, args); isInvoked false; }, delay); } }; debounced.cancel function() { if (timer) clearTimeout(timer); timer null; isInvoked false; }; return debounced; }另一个高频手写题是“实现一个Promise.all”。这类题考的是对Promise状态的深入理解而不是简单的“Promise是什么”。需要把握的状态机是输入的所有Promise都resolve后返回的Promise才resolve任何一个reject立即reject。同时要处理输入是空数组、输入包含非Promise值、以及resolve结果的顺序必须与输入顺序保持一致function promiseAll(promises) { return new Promise((resolve, reject) { if (!Array.isArray(promises)) { return reject(new TypeError(arguments must be an array)); } const results new Array(promises.length); let count 0; if (promises.length 0) { return resolve(results); } for (let i 0; i promises.length; i) { Promise.resolve(promises[i]).then( (value) { results[i] value; count; if (count promises.length) { resolve(results); } }, (reason) { reject(reason); } ); } }); }注意这里用Promise.resolve包裹输入值是为了兼容数组里混入普通值的情况。结果数组使用固定下标赋值而不是push是为了保证结果顺序和输入顺序一致。这些细节恰恰是评分时区分度的来源。4. 一份可复盘的备战清单从真题反推知识体系4.1 知识模块自查表做完这套真题复盘我最大的感受是前端面试的知识边界其实很清晰只要按模块系统复习就不会有太大遗漏。我整理了一张自查清单包含京东这套笔试覆盖到的所有关键模块以及每个模块里最容易丢分的细节。模块核心考点最容易忽略的细节HTTP与网络状态码、缓存、跨域、HTTP/2Cache-Control与Expires优先级预检请求触发条件浏览器原理渲染管线、事件循环、存储重绘回流触发条件宏任务微任务的边界情况JavaScript语言作用域、闭包、原型链、this绑定箭头函数this穿透new绑定优先级高于apply异步编程Promise、async/await、事件循环Promise executor是同步执行的await后的代码进入微任务CSS布局盒模型、BFC、居中、两栏三栏margin塌陷条件flex的shrink默认值是1数据结构数组API、栈、队列、二叉树手写栈和队列时的shift性能问题工程化基础模块化、打包工具、GitAMD/CommonJS/ESM的运行时差异自查时不要只看“这个知识点我听过”要试着在纸上闭卷写出来。比如提到原型链能不能画出Function、Object、普通实例之间的prototype关系提到事件循环能不能写出setTimeout、Promise、process.nextTick组合时的输出顺序。笔试最大的敌人不是不知道而是“以为自己知道”。4.2 刷题之外的三个高效准备动作在我的经验里备考不光是刷题更重要的是建立“从现象到原理”的追问习惯。第一个动作给自己出变式题。做一道题之后主动改变条件再看结果。比如把var改成let把setTimeout的延迟时间从100改成0把回调函数从普通函数改成箭头函数把外层函数改成对象方法调用。每次改动都重新推理一次输出直到能完全解释为什么会变。这比一口气刷二十道同类型题更有效。第二个动作练习手写代码时关闭IDE的联想功能。笔试是在网页编辑器上进行的没有代码提示没有语法高亮甚至没有浏览器调试工具。很多人在本地开发时依赖npm run build和console.log进行验证一到笔试现场就发懵。建议在备考阶段用纯文本编辑器或者直接拿笔在纸上写代码这样才能暴露自己对API记忆的薄弱点。第三个动作整理一份“错题本”按错误原因分类而不是按题目类型分类。比如同样是写错数组去重如果你是map和Set的混用导致语法错误那说明你对ES6的API不够熟练如果你是没考虑数组中包含NaN的情况那说明你对Set的SameValueZero比较机制缺乏了解。错误原因不同对应的补救方向完全不同。4.3 考场上的时间分配与答题顺序技巧最后分享一个很实用的考场策略尤其适合京东这类题量大的笔试。先花1-2分钟扫一遍整张卷子确认每部分的题量和难度。一般我不会严格从第一题做到最后一题而是把选择题里最有把握的题先做掉因为这是最容易拿分的部分也容易建立信心。遇到需要手写代码的题先不要急着写现在草稿纸上写一个最小实现确认逻辑正确后再誊写到答题区。手写代码题有个很关键的动作在动手之前先用文字列出你能想到的边界条件。比如“输入为空时怎么办”“输入的Promise数量为0时怎么办”“this应该怎么绑定”。写下来之后再开始写代码。这样即使代码没有完全跑通阅卷人也能从你的注释和边界条件列表里看到你的思考过程这比一段孤零零的代码得分高得多。京东2018秋招这套笔试题放到今天看依然没有过时。它考察的JavaScript执行机制、浏览器渲染原理、异步编程和手写能力依然是前端面试的核心。很多同学问我要不要背答案我的建议是千万不要背。把每一道题当成一个引子去扩展出背后完整的知识树才是这套题最大的价值。我自己当年准备面试时最受益的动作就是把每个考点都追问一遍“为什么”直到能给自己讲明白为止。
返回列表