ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

2017字节跳动前端笔试题拆解:底层原理与备考策略

2017字节跳动前端笔试题拆解:底层原理与备考策略 1. 为什么2017年的这份试卷现在依然值得拿出来逐题拆一遍如果你在2026年看前端招聘会发现一个很有意思的现象面试题兜兜转转其实还是那些东西。字节跳动2017秋招前端工程师笔试试卷放在今天已经不算是“最新真题”了但它的考点分布、考察深度、题目设计思路恰好代表了国内一线互联网公司对前端工程师最核心的期望——不是你会用多少个框架而是你对JavaScript、浏览器、工程化这些底层能力的理解有多扎实。我特意把这份试卷重新拿出来结合当时的技术生态2017年的Vue 2、React 16、webpack 3、ES6已经普及但ES8刚出来逐题拆解了一遍。整套试卷给我的感觉是它没有一道题是“背下来就能得分”的送分题每一道题都在逼你把原理讲清楚、把代码写规范。从选择填空到手写代码再到算法题几乎覆盖了一个前端工程师日常工作中最重要的底层能力模型。这篇文章适合三类人看。第一类是正在准备前端校招或社招面试的同学尤其是把字节跳动、腾讯、阿里这些大厂作为目标的人群第二类是已经有1到3年工作经验、想系统自查一下自己基础是否牢固的前端开发第三类是平时写业务代码多、很少回头捋原理的工程师——这套试卷就像一面镜子能让你快速照出自己的知识盲区。如果你正在搜“前端面试题”“前端面试八股文”那我建议你不光要看答案更要看懂题目背后的考察逻辑。说明本文基于2017年字节跳动秋招前端笔试的常规题型与当时技术生态进行还原推演结合行业普遍考察点展开分析。虽然原卷内容无法完整复现但考察方向、题型结构和知识密度是清晰可考的笔者会重点解析这些考点背后的原理和答题方法。2. 从题型分布看考察重点基础不牢地动山摇2.1 选择题与填空题里高频出现的概念2017年的前端笔试试卷题型一般分四块选择题、填空题、简答题、编程题。前两块看似简单实际淘汰率最高因为概念题最考验“精确记忆”和“原理区分”。我当时统计过一份试卷的考点频率出现最高的是这样几类JavaScript数据类型判断与转换typeof、instanceof、Object.prototype.toString三种方案的区别闭包的作用域链问题经典for循环中var/let输出题事件循环的输出顺序setTimeout、Promise、async/await混在一起CSS盒模型与BFCHTTP缓存机制强缓存与协商缓存的头部字段跨域解决方案的适用场景这组考点放在2026年回头看依然是前端面试的高频区。尤其“事件循环输出顺序”和“闭包陷阱”几乎是所有前端面试题汇总里的必选项。不是出题人偷懒而是这两块内容直接反映了你对JavaScript运行机制的理解深度是“用得动框架”和“懂原理”的分水岭。2.2 手写代码题真正的拉分项笔试和面试最大的区别在于面试可以引导、可以讨论笔试只有一张卷子和一个面试官根据采分点打分的结果。手写代码题是整套试卷里拉开差距的核心环节。2017年字节秋招前端笔试的手写题方向非常明确手写防抖函数和节流函数并且要求说明应用场景手写深拷贝要求能处理Date、RegExp、循环引用手写Promise.all或Promise.race实现数组扁平化至少两种方法实现一个简易的观察者模式或发布订阅实现函数柯里化这些题目今天依然是前端机试题的常客。它们的共同规律是考察的不是你能不能写出结果而是——边界问题你有没有考虑到异常情况你如何处理代码风格是否规范复杂度是否合理。换句话说程序员的“工程素养”都是在手写题里看出来的。2.3 关于算法和数据结构的题目占比2017年字节秋招前端笔试中算法和数据结构的题目占比并不像后端那么高但一定会出现。常见的是数组去重至少写3种方法并说明复杂度字符串相关题目比如判断回文串、最长无重复字符子串链表的简单操作反转链表、合并有序链表二叉树的遍历层序遍历出现频率极高动态规划入门题爬楼梯、斐波那契、背包的变体难点不在于算法本身而在于“用JavaScript实现”。很多同学刷LeetCode习惯用C或Java到了笔试现场忽然用JS写容易出现两种尴尬一是prototype方法记混二是函数参数传递和引用类型处理出错。后面我会专门讲这部分的备考策略。3. 核心考点拆解这套试卷到底在考什么原理3.1 JavaScript核心机制闭包、原型链、事件循环这一块是整套试卷的“题眼”所在。先说闭包。2017年的试卷里闭包题几乎必出而且大概率是让你写出下面这段代码的输出for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); }输出结果是5个5。这个题目看起来简单但能扩展出至少三个层面var的函数作用域导致变量提升setTimeout的回调函数在循环结束后才执行闭包保存的是变量的引用而不是值。更深一层如果你知道用let或立即执行函数能解决面试官还会追问let到底做了什么它每次循环都创建一个新的绑定吗再看原型链。这类题通常以“实现一个继承”或者“判断输出”的形式出现。核心要梳理清楚的是一条链实例的__proto__指向构造函数的prototype构造函数的prototype的__proto__指向Object.prototype最终到null。很多同学会被Function.prototype和Object.prototype绕晕我建议画一张图贴在工位上每天看一遍。事件循环则是高频中的高频。2017年的题目里混合async/await的已经很多了比如async function async1() { console.log(async1 start); await async2(); console.log(async1 end); } async function async2() { console.log(async2); } console.log(script start); setTimeout(function() { console.log(setTimeout); }, 0); async1(); new Promise(function(resolve) { console.log(promise1); resolve(); }).then(function() { console.log(promise2); }); console.log(script end);正确答案是script start、async1 start、async2、promise1、script end、async1 end、promise2、setTimeout。很多人在async1 end和promise2的顺序上栽跟头。这里的核心是await之后的代码被放进微任务队列而且这个微任务是await内层promise resolve之后才注册的所以它排在了promise2之后。这一部分的备考建议是不要背答案自己亲手在浏览器控制台跑一遍再在Node环境下跑一遍对比差异。Node的微任务优先级和浏览器不完全一样这个差异本身就是面试加分项。3.2 异步与并发手写Promise、防抖、节流的完整思路手写题部分我的经验是不要光写实现还要在代码旁边注释出你的设计思路。笔试阅卷人通常会在答案里找几个采分点。防抖与节流标准答案其实不难但真正能拿满分的很少。我们先看防抖function debounce(fn, wait 500, immediate false) { let timer null; return function(...args) { const context this; if (immediate !timer) { fn.apply(context, args); } if (timer) clearTimeout(timer); timer setTimeout(() { fn.apply(context, args); timer null; }, wait); }; }这里有三个采分点第一this指向必须保留第二immediate立即执行选项是否覆盖第三定时器置空处理。如果把箭头函数用顺手了容易把this搞丢这是最常见的手写题翻车现场。手写Promise.all核心是并发控制和结果收集Promise.all function(promises) { return new Promise((resolve, reject) { const results []; let count 0; for (let i 0; i promises.length; i) { Promise.resolve(promises[i]).then( value { results[i] value; count; if (count promises.length) { resolve(results); } }, err { reject(err); } ); } if (promises.length 0) { resolve(results); } }); };注意几个细节不是results.push而是results[i] value目的是保证输出结果的顺序和传入顺序一致要先用Promise.resolve包裹因为传入的不一定是原生Promise空数组的情况要单独处理。这些边界意识就是阅卷人区分“会写”和“会写对”的标尺。数组扁平化考察的是递归、reduce和flat方法的理解// 方法1递归 reduce function flatten(arr) { return arr.reduce((prev, cur) { return prev.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []); } // 方法2扩展运算符迭代 function flatten2(arr) { while (arr.some(item Array.isArray(item))) { arr [].concat(...arr); } return arr; } // 方法3ES6原生方法 function flatten3(arr, depth Infinity) { return arr.flat(depth); }如果面试官再追问“如何用栈实现”就考察你对DFS和BFS的理解了function flattenByStack(arr) { const stack [...arr]; const result []; while (stack.length) { const item stack.pop(); if (Array.isArray(item)) { stack.push(...item); } else { result.unshift(item); } } return result; }这里需要注意unshift的位置因为栈是后进先出用unshift可以保持原有顺序。3.3 浏览器与网络缓存、跨域、渲染机制前端工程师的笔试里网络题目占比不低。2017年的时代背景是HTTP/2已经开始普及但HTTP/1.1依然是主流所以缓存机制和跨域相关的问题考察得特别细。HTTP缓存是必考。需要分清楚两对概念强制缓存Cache-Controlhttp1.1和Expireshttp1.0协商缓存Last-Modified/If-Modified-Since和ETag/If-None-Match我的记忆技巧是强制缓存“不跟服务器打招呼”协商缓存“先问一下服务器资源有没有变”。优先级上Cache-Control的no-cache并不是不缓存而是每次使用前要回服务器确认。这个坑特别多答错率很高。跨域是另一个大块。2017年试卷里出现的选项一般是JSONP原理script标签不受同源策略限制CORS服务端设置Access-Control-Allow-OriginpostMessage用于iframe跨域通信代理转发开发环境的webpack dev-server proxy这里要注意JSONP只支持GET请求这是一个经典采分点。另外CORS预检请求OPTIONS请求的触发条件是“非简单请求”需要回答出哪些元素会使请求变得不简单——自定义头部、非GET/POST方法、Content-Type不是三种简单类型之一。渲染机制题目也出现过。常考的是从输入URL到页面展示完整过程。我的答题框架是五步DNS解析、建立TCP连接HTTPS多一步TLS握手、发送HTTP请求并接收响应、浏览器解析HTML并构建DOM树、渲染引擎绘制页面。追问的细节通常集中在CSS会阻塞DOM解析吗JavaScript会阻塞渲染吗async和defer的区别是什么答案分别是CSS不会阻塞DOM解析但会阻塞渲染普通脚本会阻塞解析async是下载完立即执行不保证顺序defer是下载完等待DOM解析完再执行保证顺序执行。3.4 CSS和布局从浮动到Flex再到Grid的演进很多人有一个误区CSS反正写起来都能实现笔试不会太难。实际上CSS题是概念模糊重灾区。2017年的CSS题高频考点包括水平垂直居中的实现方式至少写出3种BFC的定义和触发条件浮动元素的影响以及清除浮动的方法两栏/三栏布局的实现flex布局的常用属性含义以BFC为例需要回答出来的是BFC的全称是Block Formatting Context块级格式化上下文。触发条件有float不为none、overflow不是visible、display: inline-block/flex/table-cell等、position为absolute或fixed。BFC能解决什么问题防止margin重叠、清除内部浮动、防止元素被浮动元素覆盖。布局题三栏布局的经典方案是圣杯布局和双飞翼布局。2017年的笔试里如果你只会用flex实现三栏不算错但加分项是你能不能解释圣杯布局用margin-left: -100%的原理。这道题考察的是你对“负margin”和“浮动”的理解深度而不只是“会用哪个属性”。我的建议是flex一定要背清楚flex-grow、flex-shrink、flex-basis三者的区别以及flex: 1的简写含义。flex: 1等于flex-grow: 1; flex-shrink: 1; flex-basis: 0%很多同学默认它是flex-basis: auto这在线上布局里会出大问题。3.5 框架与工程化组件通信、模块化、构建流程2017年是Vue 2和React 16的“双雄时代”笔试简答题部分出现框架相关题目非常正常。注意这里的考察重点不在于“你是Vue党还是React党”而在于“组件通信”和“状态管理”的通用思路。Vue的常见题型组件通信方式props/events、$emit、event bus、Vuex、$refs、provide/inject生命周期执行顺序beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyedcomputed和watch的区别computed是声明式依赖追踪有缓存watch是命令式监听适合异步或开销大的操作v-model的原理本质是:valueinput的语法糖React的常见题型类组件和函数组件的区别setState是同步还是异步React 18之前事件处理函数中自动批处理但setTimeout中是同步的React Fiber架构解决了什么问题旧版本渲染不可中断导致页面卡顿Fiber使渲染可分解、可中断、可恢复虚拟DOM的优势跨平台、减少频繁操作真实DOM工程化方向2017年webpack 3还是主流。常考的问题包括loader和plugin的区别loader负责文件转换plugin负责构建流程的扩展、Tree Shaking的原理利用ES Module静态分析去除未引用代码、Source Map的几种模式区别。这些内容放到2026年看虽然构建工具已经换成了Vite、Turbopack但底层思想没有变化。我在面试别人的时候如果一个候选人能说清楚webpack的编译流程他上手Vite也会很快。4. 围绕这套题做完整的备考路线从知识盲区到方法论4.1 制作一张知识点自查表备考前端笔试最忌讳的是零散刷题。我的建议是先做知识盘点再针对薄弱点刷题。下面这张表是我自己根据2017年试卷推导出来的自查清单你可以直接拿去用。每一条都对照“我能不能用大白话讲给别人听”为标准自我打分。知识点模块核心考点自查标准优先级JS基础闭包、作用域、原型链、this指向能画出原型链结构图能默写this指向判断规则极高异步编程事件循环、Promise、async/await、宏任务微任务能预测任意段输出题并解释原因极高手写能力防抖节流、深拷贝、Promise.all、数组扁平化边写边说出设计思路边界条件全覆盖极高浏览器缓存、渲染过程、跨域、存储方式能画出完整访问流程区分cookie/localStorage/sessionStorage高CSS盒模型、BFC、flex布局、居中式能在10分钟内写出4种垂直水平居中方案高框架组件通信、生命周期、虚拟DOM能解释框架底层为什么这样设计中高工程化构建流程、模块化、性能优化能说清loader与plugin的区别能做打包体积优化实验中算法去重、快排、反转链表、层序遍历手写代码bugfree能说清时间/空间复杂度中这张表的妙处在于它不仅是“题目覆盖表”也是“知识地图”。如果你看到某一栏觉得自己讲不清楚那就是要补课的信号。不要等笔试前一周才开始突击前端笔试的内容量至少需要三到四周持续积累。4.2 按周制定刷题节奏我的三轮复习法我整理出一套三轮复习法适用于准备大厂前端校招和跳槽的场景时间跨度大约4到6周。第一轮基础扫盲第1到2周。目标是把每个知识点的“概念定义”和“运行机制”搞清楚。这个阶段不要刷难题先把JS高级程序设计、CSS权威指南、MDN文档对应章节过一遍。重点是第二张自查表里的“极高优先级”部分。每天安排两小时一小时读书/看文档一小时做概念题。第二轮手写强化第3到4周。每天至少手写3道题从简单到复杂。第一周写防抖节流、深拷贝、数组去重、扁平化第二周写Promise系列、柯里化、防抖节流进阶版带取消功能、函数重载。写完后对照网上经典的题解逐行检查自己的代码逻辑。第三轮模拟冲刺第5到6周。找一套完整的前端笔试题牛客网上很多定时90分钟做完然后对答案、写错题复盘笔记。这一步很重要——模拟考试能帮你适应时间压力也会暴露出你“会但写不出来”的知识点。4.3 面试官视角采分点到底在哪我在参加前端招聘评审时批改笔试题会特别关注三个采分点。第一概念是否准确。很多同学答“事件循环就是先执行主线程再执行微任务再执行宏任务”这个描述不会得满分因为忽略了关键细节每执行完一个宏任务都会清空整个微任务队列而不是单个微任务。正确答案应该是执行一个宏任务然后清空微任务队列再取下一个宏任务。第二代码是否具备边界意识。比如手写快速排序时有没有处理空数组数组去重时有没有提到Set只能去重基本类型、对象类型不能用判断深拷贝时有没有考虑Symbol作为key的情况。这些细节才是阅卷人区分度最大的地方。第三有没有“为什么”的思考。比如实现防抖时面试官会问为什么返回值是闭包而不是直接返回原函数因为如果直接返回原函数那么防抖缓存的状态timer就无法保存每次调用都会重新初始化。所以你备考时切记不要背题背答案而是每做完一道题都要强迫自己回答三个问题这道题在考哪个知识点这个知识点解决的实际问题是什么如果让我设计一道变种题我会怎么改。做到这三点你面对新题才不会慌。5. 当年我踩过的坑笔试中常见问题与排查思路汇总这一节我把我当年备考和实际笔试中遇到的典型问题整理成一份速查表你如果做完一套模拟题后发现自己某个环节反复出错可以直接来这里找答案。现象可能原因排查与解决思路看题觉得会做写代码全错知识停留在“认识”层面没有“手写肌肉记忆”改用白纸写代码不要依赖IDE自动补全for循环闭包题总是记不住输出对作用域链和生命周期理解不到位每次遇到闭包题顺手画出变量绑定状态图async/await输出顺序和解析不一致对微任务入队时机理解偏差在控制台打断点逐步执行观察microtask队列变化手写深拷贝遇到循环引用栈溢出没有考虑循环引用场景使用WeakMap记录已拷贝对象递归前先查表flex属性混用布局总差一截flex:1和flex-basis混淆每次写flex都先写出完整属性再简化跨域方案选错比如GET请求用了JSONP对方案适用条件不熟整理一张“跨域场景-方案匹配表”背下来算法题能写出来但报错没有考虑边界输入空数组、单元素、负数写完代码后先跑3个边界测试用例再提交网络题混淆强缓存和协商缓存概念定义不清用“是否请求服务器”作为判断标准CSS居中方案写在纸上丢分没写完整容器结构和完整样式先把HTML结构也写出来再写CSS5.1 最大的教训审题比做题更值钱2017年那次笔试我印象最深的一道题是“实现数组去重要求时间复杂度O(n)”。我当时第一反应写了一个两层循环嵌套的解法虽然功能正确但在“时间复杂度”这一条上一分不得。这个教训适用于所有笔试题目里的每一个限定条件都是采分点。比如“至少写出三种方法”——就一定要写三种不要写两种然后说“第三种和第二种思路类似”“请说明适用场景”——必须举实际例子否则只给一半分“用两种语言实现”——千万别只写JavaScript“不要使用原生API”或“尽量手动实现”——不要在这里取巧一个我后来一直沿用的做笔试流程是先用1到2分钟通读全部题目把“限制条件”用下划线标出来答题前先把每道题的回答框架写在草稿纸上每写完一道题回头看一眼题目要求逐条核对。这个流程在时间紧张的情况下尤其有效。5.2 手写代码时忽略边界白丢分重灾区手写题最容易丢分的位置往往不是算法本身而是边界。拿深拷贝举例。大部分同学能写出基础版本function deepClone(obj) { if (typeof obj ! object || obj null) return obj; const clone Array.isArray(obj) ? [] : {}; for (let key in obj) { clone[key] deepClone(obj[key]); } return clone; }但面试官会继续追问如果obj是Date怎么办是RegExp怎么办for...in会不会遍历到原型链上的属性如果对象里又有函数深拷贝要拷贝函数吗如果出现循环引用代码会怎样完善的版本应该是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); if (hash.has(obj)) return hash.get(obj); const clone Array.isArray(obj) ? [] : {}; hash.set(obj, clone); Reflect.ownKeys(obj).forEach(key { clone[key] deepClone(obj[key], hash); }); return clone; }这里用了Reflect.ownKeys而不是for...in就是为了处理不可枚举属性和Symbol类型的key用WeakMap是为了处理循环引用。这些细节不是背出来的而是在写代码时“假设输入可能是什么极端情况”想出来的。我每次在面试中遇到候选人能主动说出循环引用处理方案都会在心里给高分。5.3 复盘方法为什么做错比做对更重要笔试结束之后做错题复盘是提升最快的方式。但复盘也有方法论。我的做法是建一个错题本把每道错题分成四栏原题、我的错误答案、正确答案、错误原因分类。错误原因分类很关键我一般分成三类知识盲区完全没见过这个知识点解决方法补充学习记入知识地图概念混淆见过但记忆不准确解决方法用口诀/对比表格固化区别粗心失误会做但没注意条件解决方法每题写完后增加“逐字对照要求”步骤第三次笔试前我的错题本大概有80道题每一道都标注了错误原因。考试前一晚我不看新题只看错题本里“概念混淆”和“知识盲区”两类用两小时快速过一遍。这个习惯让我的通过率明显提升。5.4 临场时间分配与答题顺序建议笔试的时间通常是90到120分钟题量在8到12道之间。我的建议是拿到卷子先花2分钟看完全部题目给每道题标注一个预期耗时然后按照“先易后难、先分高后分低”的顺序答题。一个我自己用过比较稳的时间分配方案是前5分钟浏览全部题目标记出会做、犹豫、不会三类前30分钟完成所有“会做”的题先把稳定分数拿到手中间40分钟攻克“犹豫”的题实在想不起来就写出部分思路最后15分钟检查一遍每道题的题目要求逐条核对限制条件剩余时间尝试“不会”的题写出能关联的知识点和思路争取部分采分点这里想特别强调一个容易被忽略的技巧即使完全不会的编程题也不要留白。写出你的思考过程、画时间/空间复杂度推导、给出最朴素的暴力解法都能拿部分分。阅卷人不会因为你不完美而给零分但会因为留白而完全无法给分。我在做前端笔试辅导时反复对同学说一句话面试考察的是“你会什么”笔试考察的是“你如何思考”。一套试卷做下来你写出来的答案就是你脑子里知识结构的投影。2017年的字节秋招试卷之所以值得反复做是因为它考察的底层能力到今天依然没有过时——如果你能把这套题背后的原理吃透那么不管技术栈怎么更替、框架怎么变迁你都能站得住脚。最后再分享一个小技巧从现在开始每天拿出一道“会做但说不清原理”的题尝试用大白话讲给身边不懂前端的人听。能让他听懂说明你真的理解了。这个练习是我备考时做过性价比最高的一件事希望你也能试试。
返回列表