ARTICLE DETAIL

资讯详情

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

前端实习笔试考点全解析:JS基础、闭包、事件循环与Vue备考指南

前端实习笔试考点全解析:JS基础、闭包、事件循环与Vue备考指南 我记得那天晚上打开在线笔试链接之后整个人对着一个空荡荡的文本框懵了很久。那道题要求手写一个带防抖功能的函数并且要正确处理this指向和参数透传。日常开发里这不是什么难事可用处在一个没有自动补全、没有任何提示的网页编辑框里心里那点底气一下子就虚了。那是小米2018春季实习生前端开发工程师笔试也是我第一次真正意识到准备前端开发面试题不能靠背得靠理解。后来我把那套题里涉及的考点全部重新梳理了一遍发现一份笔试考卷其实就是一张前端开发技能的体检表每一个考点背后都对应着实际开发中的一个真实场景。今天这篇文章我就把这场笔试的题型、考点和复习思路完整拆开给正在准备 Web 前端开发实习的同学提供一份可以直接照着用的清单。1. 2018春季实习生前端笔试的整体画像与考察逻辑1.1 第一次笔试现场在线判题系统里的真实体验先说说整体形式。那年小米的春季实习生笔试前端开发工程师岗给我的感觉是题量不算特别大但时间很紧张。整场考试大概还是走了“选择题 填空题 手写代码题 场景设计题”的常见组合限时大概在 90 到 120 分钟这个区间。选择题里会穿插一些代码输出题需要你在几个选项中判断运行结果手写代码题则要在一个文本框里写出完整实现没有运行环境没有 lint甚至没有格式化。我印象最深的不是某道特别偏的题而是环境本身。它逼着你在写代码之前先在脑海里把逻辑完完整整跑一遍。因为一旦下手你很难像平时开发那样一边 console.log 一边调试只能在有限的时间内靠脑内编译。这个经验很残酷但也很有价值它逼着我把“会写”变成“写对”。所以如果你现在准备这类笔试我建议平时就刻意练习“不看编辑器写代码”。可以开着记事本写也可以直接在纸上写写完再复制到 IDE 里验证。这个过程会让你迅速发现自己在 API 细节、边界条件、标准写法的记忆上还有多少漏洞。1.2 命题逻辑要从一场笔试里看出什么我复盘时想明白了一个事笔试不是用来出难题难倒你的它更像个漏斗第一层筛掉基础不扎实的人。大厂招聘实习生最怕的不是候选人不会某个冷门技术而是候选人连 JavaScript 里非常核心的概念都搞不清。实习生进来是要跟项目产出的如果连作用域、闭包、异步都说不明白那带起来会非常辛苦。所以你会发现笔试题看似在考某个 API实际上都在考底层理解。比如var和let的区别表面上是语法差异背后是作用域和变量提升比如setTimeout的输出顺序表面上是异步问题背后是事件循环和任务队列比如box-sizing的取值表面上是 CSS 属性背后是盒模型理解。这套逻辑到今天依然成立。前端框架一年换一批但 JavaScript 的核心机制、浏览器工作原理、网络协议基础这些是不会一夜之间失效的。笔试真正想筛出来的是那些“遇到新问题也能用基础能力推演”的人。2. 核心考点拆解从JavaScript到浏览器原理2.1 JavaScript基本功变量提升、闭包和thisJavaScript 基础在任何一次前端开发面试题里都是绝对的主角小米这次也不例外。我印象里有几类题几乎必出。第一类是变量提升和函数声明优先级。比如console.log(a); // 输出什么 var a 10;答案当然是undefined因为var会把声明提升到作用域顶部但赋值不会。这里还有一个延伸考点function foo() { return 1; } var foo; console.log(typeof foo); // 输出什么答案是function因为函数声明比变量声明优先级更高。如果对这两个概念不够熟悉笔试里很容易丢分。第二类是闭包。闭包几乎是所有前端笔试的保留节目考察方式非常多。最常见的是循环和异步结合在一起for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 100); }输出是 5 个 5而不是 0 1 2 3 4。原因是var没有块级作用域循环里的setTimeout回调共享同一个i变量。等到 100ms 之后执行时循环已经结束i已经变成了 5。当年我看到这道题第一反应是“怎么会想到这么刁钻的考法”。后来才明白这就是真实开发里容易踩的坑异步回调里读取了一个循环变量结果全是同一个值。解决方法也很经典可以用 IIFE 立即执行函数或者 ES6 的letfor (let i 0; i 5; i) { setTimeout(() console.log(i), 100); }第三类是this指向。考题通常会给一段代码让你判断输出比如function getName() { console.log(this.name); } const obj { name: 小米, getName: getName }; obj.getName(); // 输出什么 const fn obj.getName; fn(); // 输出什么第一个输出小米因为调用方式是obj.getName()this指向obj第二个输出取决于全局环境下有没有name变量在严格模式下就是undefined非严格模式下可能指向window.name。核心判断标准就是一句话看函数被谁调用调用点决定了this指向。这里还经常延伸到箭头函数箭头函数没有自己的this它会继承定义时所在作用域的this。2.2 函数进阶call/apply/bind与...args的考点前端开发里经常有人问“函数里的...args是什么”这个问题我在那年的笔试复习资料里也看到了。它其实是剩余参数Rest Parameters顺便还和扩展运算符Spread一起出现。笔试不会直接问你定义而是会放在代码题里让你填写。比如function sum(...nums) { return nums.reduce((a, b) a b, 0); }这里的...nums会把所有参数收集成一个数组。还有一个方向是把数组展开成参数列表const arr [3, 5, 1]; Math.max(...arr); // 5 Math.max.apply(null, arr); // 5这里就带出了call、apply、bind的差异。call和apply都会立刻执行函数区别仅仅是参数传递方式不同call是逐个传apply是传数组。bind不执行它返回一个新函数并且会把传入的this固定下来。笔试里更高频的是让你手写一个bind或者写一个“让类数组对象也能使用数组方法”的功能。比如function getArgs() { return Array.prototype.slice.call(arguments); }这段代码就是利用call借用数组的slice方法把arguments这个类数组对象转换成真正的数组。在 ES6 里可以直接用Array.from(arguments)或者[...arguments]完成但理解底层的借用思路依然重要。如果你能自己手写一个bind那这部分的印象分会涨不少Function.prototype.myBind function(context, ...args) { const fn this; return function(...rest) { return fn.apply(context, args.concat(rest)); }; };这里面包含了两层参数合并绑定时的参数和执行时的参数要拼在一起同时还要保证返回函数被new调用时的表现。笔试不一定会让你写这么完整但如果能写出来说明你对函数调用的底层机制是真的理解到位了。2.3 异步编程与事件循环笔试里的“送分题”与“送命题”前端一天到晚都在跟异步打交道所以事件循环是必考项。笔试最典型的是一道输出顺序题console.log(A); setTimeout(function() { console.log(B); }, 0); Promise.resolve().then(function() { console.log(C); }); console.log(D);正确输出顺序是A D C B。很多人会搞错C和B的前后原因在于事件循环里微任务和宏任务的机制。同步代码先执行完然后清空微任务队列Promise.then属于微任务会排在setTimeout这个宏任务之前执行。我当时复习的时候会把任务队列画成一张图。画一次你就理解了每次事件循环都会先取出一个宏任务执行执行完后再把所有微任务清空如果微任务里再产生微任务会一起清完再进入下一次宏任务。所以setTimeout虽然设的是 0ms也只能在微任务之后执行。另外常见的进阶题是async/await结合Promise的推导题async function test() { console.log(1); await Promise.resolve(); console.log(2); } test(); console.log(3);输出是1 3 2因为await后面的代码相当于放进了.then()里属于微任务。这些题看着绕但只要把“同步执行完再清微任务”的规则吃透就不会错。笔试考这个不是在秀偏门语法而是因为在真实项目里请求数据、DOM 更新、页面渲染、动画执行全都依赖事件循环的调度。不懂它写出来的代码就会出现“明明先发出去的请求结果后到的数据覆盖了先到的数据”这种经典 bug。2.4 CSS布局、盒模型和前端页面实现前端开发面试题里不一定全是 JavaScriptCSS 也占了不少篇幅。我印象里那年笔试至少有这么几个方向。第一个是盒模型。题目会问content-box和border-box的区别或者让你算一个元素的实际宽度。content-box是默认值宽度只包含 contentpadding 和 border 会往外撑border-box是宽度包含 content、padding、border实际布局更可控。现在很多项目都会在全局设置* { box-sizing: border-box; }就是为了避免 padding 把布局撑乱。第二个是居中问题。垂直水平居中可以说是前端笔试里最经典的 CSS 题。常见方案有 flex.parent { display: flex; align-items: center; justify-content: center; }也可以使用绝对定位加 transform.child { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); }如果元素尺寸固定还可以用负 margin。关键是搞清楚每种方案的使用条件flex 对子元素数量不确定的场景很舒服transform 不要求知道元素宽高但会生成层叠上下文。笔试里如果要求“不用 flex 实现”说明出题人想考你对定位和 margin 的理解。第三个是响应式。可能会问你 rem 和 px 的区别或者媒体查询怎么写。这类题主要考察你平时做页面时有没有考虑过多尺寸适配。复习时可以重点准备一下 rem 计算方式以及flex布局在移动端常用的几个属性。2.5 网络、缓存与性能优化题前端笔试如果只考代码那是不完整的。那年的题目里也有不少网络和浏览器相关的内容。比较常见的是 HTTP 缓存机制分强缓存和协商缓存。强缓存相关的头是Expires和Cache-Control协商缓存相关的头是Last-Modified/If-Modified-Since和ETag/If-None-Match。有一道经典连环题用户在地址栏输入一个 URL 到页面展示整个过程发生了什么这个题很适合用来复习因为知识点特别集中DNS 解析、TCP 连接、HTTP 请求、服务器响应、浏览器解析 HTML、构建 DOM 树、CSSOM 树、执行脚本、布局和绘制。笔试可能只考其中一段比如“为什么 CSS 放在头部JS 放在尾部”或者“什么是渲染阻塞资源”。答案一句话就能概括CSS 会阻塞渲染JS 会阻塞解析所以 CSS 要尽早加载JS 可以延迟执行或用defer。这种常识虽然简单但背后是浏览器渲染流程的理解还是值得认真整理的。性能优化方向也常考比如懒加载、图片压缩、CDN、HTTP/2、代码分割。我当时的复习思路是记住一个优化链路减少请求体积减少请求数量提升加载并行度减少主线程阻塞。有了这个框架笔试里遇到具体优化题就能往里面套不会太空洞。3. 备考核心Vue、手写题与工程化思维3.1 Vue技术栈数据响应式、组件通信与开发规范2018 年那会儿Vue 已经是大厂实习笔试里的高频词了。小米的笔试题虽然没有直接让写一个组件但围绕 Vue 的知识点出现得不少比如数据响应式原理、组件通信方式、生命周期顺序等。数据响应式是我复习得最深的一个点。Vue 2 的响应式核心是Object.defineProperty它通过 getter/setter 拦截属性的读取和修改再配合依赖收集和派发更新让数据变化自动触达视图。面试里会进一步问Object.defineProperty有什么缺点答案大家应该也都知道无法监听属性的新增和删除对数组的索引变化无法感知。所以 Vue 2 才需要提供$set和$delete这种补丁式 API。Vue 3 改用Proxy之后这些问题就迎刃而解了。组件通信也是必背清单。父子之间靠props和$emit兄弟组件靠事件总线复杂项目可以上 Vuex。这里面会延伸出一个问题props为什么不能直接修改因为它会造成数据单向流被破坏后续的数据状态变得不可预测。笔试如果只让你回答“能不能改”你最好能解释清楚“为什么不能改”。还有一个容易被忽略的是 Vue 开发规范。虽然笔试不会考具体配置文件但会从一个更高层面问怎么保证一个项目长期可维护这就要涉及组件命名规范、目录结构划分、ESLint 统一代码风格、状态管理按模块拆分等。热词里提到的“前端开发规范vue”本质上就是在说框架能力只是起点真正拉开差距的是你在团队协作里能不能写出一致、清晰、可维护的代码。3.2 高频手写题数组去重、防抖节流、Promise.all手写题是笔试里最考验人的部分因为它没法蒙。我那年的复习范围里有几类是反复出现的。数组去重。最简单的写法当然是 ES6 的Setconst unique arr [...new Set(arr)];但笔试如果只写这种说明你在基础层够了但在“展开思考”上还不够。可以再写一个reduce版本或者手写一个时间复杂度 O(n) 的对象标记方式顺便讨论一下不同数据类型的去重问题。这会让面试官看到你思维的层次。防抖和节流。这两个高频题也是我在笔试现场翻车的点。防抖是“最后一次触发后才执行”节流是“固定时间内只执行一次”。代码模板如下function debounce(fn, wait) { let timer null; return function(...args) { clearTimeout(timer); timer setTimeout(() { fn.apply(this, args); }, wait); }; } function throttle(fn, wait) { let previous 0; return function(...args) { const now Date.now(); if (now - previous wait) { previous now; fn.apply(this, args); } }; }写成这样之后还要能解释什么时候用哪个。搜索框输入建议用防抖因为等你停下来再去请求滚动加载或按钮点击限频用节流因为要保持一个节奏。Promise.all。这个手写题我当时背了很多遍核心思路是等待所有 Promise 都完成并且结果按传入顺序排列function promiseAll(promises) { return new Promise((resolve, reject) { const results []; let count 0; if (promises.length 0) { resolve(results); return; } promises.forEach((p, index) { Promise.resolve(p).then(value { results[index] value; count; if (count promises.length) { resolve(results); } }, reject); }); }); }这里有一个关键点结果数组不能直接 push因为 Promise 完成的顺序不一定是传入顺序所以要用results[index]去填充。手写完了还要准备口头追问如果其中一个 Promise 失败会怎样答案是走 reject并且不会继续等待其他 Promise。这种边界问题在笔试后的面试环节极容易问到。3.3 工程化思维从“会写页面”到“会组织项目”笔试里出现开放式题目时通常不是在考你某个 API而是在考你工程化意识。比如“如果让你设计一个商品列表页你会怎么拆分组件”或者“你会如何设计一个前端项目结构”。这种题没有标准答案但可以判断你的组织能力。我准备这类题的心得是养成一个“组件思维”把页面拆成可复用的最小单元再考虑每个单元的数据输入和事件输出最后再考虑状态提升到哪一层。这个思路放到 Vue、React、原生 JS 里都通用。工程化还包括模块化、构建工具、代码规范、自动化测试。笔试不一定直接让你写测试但可能问“如何保证模块之间的依赖关系清晰”或“如何减少打包体积”。我当时把 Webpack 的几个核心概念比如 entry、output、loader、plugin花了一天时间彻底理清到了笔试里遇到相关选择题基本一眼就能选对。后来我在企业级项目里还接触过 HZero 这类集成度很高的中后台开发平台它把权限、路由、页面配置都标准化了但如果你不理解前端工程的整体脉络用这些平台也只是一知半解地按文档填配置。底层组织能力最终还是从基础工程化思维里长出来的。4. 复盘后的备考方法论题源、题库和路线4.1 建立“错题—知识点—场景”三级题库备考前端开发面试题最忌讳的是拿着一堆题从早刷到晚刷完就忘。我经过这次笔试后整理了一个三级题库第一级是错题本身第二级是它对应的底层知识点第三级是这个知识点在真实开发里会出现在什么场景。比如遇到“闭包循环输出”这道题你会写答案之后应该继续问自己两个为什么为什么var没有块级作用域为什么let能解决这个问题然后再联想一个真实场景在循环里给一批按钮绑定点击事件如果回调里访问了循环变量会不会出现同样的 bug这样一来你复习的就不是一道孤立题目而是一整片知识。笔试题目背后考点真实场景循环 setTimeout 输出作用域、闭包、异步循环绑定事件、异步数据索引函数防抖手写this、闭包、定时器搜索框输入、窗口 resizePromise.all 手写异步并发、数组操作多个并行请求后再渲染页面输入 URL 全过程网络、渲染原理首屏优化、性能排查这种表格式的整理方式建议你也试一下。它能让知识不散落还能在复习时很快定位到自己的薄弱点。4.2 不要只学前端前后端协作与全栈视角那年笔试的开放题里有一个让我印象很深的场景设计一个登录流程。只懂前端的人可能会写“写一个登录表单点击后调接口拿到 token 存起来”。但真正站在项目角度思考的人会继续拆出这些问题token 过期后怎么刷新登录态要存在 localStorage 还是 cookie请求拦截器怎么统一加 token遇到 401 错误如何处理后端的接口是做了幂等设计还是需要前端防重复提交页面权限和按钮权限分别在哪个层级控制如果你能理解前端和后端的协作边界回答这类问题会一下子丰满很多。这就是很多热词里提到的“前端和后端要一起看问题”。我建议平时复习时至少要把 HTTP 请求、状态码、RESTful 接口设计、跨域方案、Mock 数据这些内容过一遍。不需要真的写 Java 或 Go但要知道一个请求从发起到后端处理再返回基本流程是什么以及后端返回的数据结构为什么会是这样。后来我用了 HZero 这类企业级开发平台时更有体会一个完整的前端功能往往不只是页面本身还涉及权限点配置、菜单路由、后端数据源对接。如果你只盯着组件库文档看很容易陷进局部忽略了整体流程。笔试开放题想看的就是你有没有这种全局视角。4.3 AI工具时代为什么基础题仍然关键站在现在的视角回头看前端开发环境已经发生了很大变化。AI 辅助编码越来越普遍很多人觉得“基础还需要手写吗”开始成为一个新问题。我在复盘时依然认为基础题不会被淘汰。因为 AI 可以帮你快速生成代码但它不能替你理解问题。比如你让 AI 写一个防抖函数它可能写得很快但当线上出现“为什么快速滚动时还是发了很多请求”的 bug你面对的是一段别人写的代码你需要有能力读懂闭包、读懂this、读懂事件循环才能定位到问题。这些能力恰恰就是笔试在考的东西。我自己的做法是把 AI 当成“带教老师”而不是“代写工具”。生成答案后我要求自己能一步步解释每一行的作用并且重新手写一遍直到完全不依赖提示。这样训练会让基础越来越稳而不是越来越依赖工具。如果你现在正准备前端开发实习我建议先别急着刷一千道题。花两天时间把 JavaScript 的核心机制理一遍把浏览器的工作原理过一遍把 Vue 或者 React 的框架核心逻辑搞懂一遍再去做题效率会高很多。基础这件事在哪个时代都不会贬值。
返回列表