爱奇艺2019秋招前端方向笔试题(A)在网上已经传了好几年,但每次拿出来看都不过时。原因很简单:这份卷子考的不是“背答案”,而是前端基础功底的扎实程度,以及遇到陌生问题时怎么拆解、怎么推导。我自己在做这份题的过程中,最大的感受就是——它把JS的语言特性、浏览器运行机制、CSS布局原理、网络协议常识、代码手写能力这几个维度全部串在了一起。如果你正在准备大厂前端岗的笔试,或者已经工作但想系统复盘一下基础,这份题都是一个很好的试金石。这篇内容我会按照笔试题的高频模块来拆解,把每类题目背后的考点、原理、踩坑点都展开讲,最后附上一些实战经验。
1. 内容整体设计与思路拆解
1.1 核心考点拆解
爱奇艺这套前端笔试题(A)的考察范围,基本可以映射到大多数中大型互联网公司前端笔试的经典结构:JavaScript语言特性、CSS布局与渲染、浏览器与网络、手写代码与逻辑题。它不会直接问“闭包是什么”这种八股,而是通过读代码写输出、补全代码、判断执行顺序等方式,考察你是否真正理解这些特性。
这套卷子里最值得注意的,是把“输出顺序题”和“原型/继承题”作为重点。这类题在牛客网上特别常见,但很多人刷题只是记答案,不去追“为什么”。比如一道经典的setTimeout与Promise输出顺序题,如果你不理解微任务和宏任务的执行时机、不理解事件循环的每一轮都会“先清空微任务队列”,那换一个变体就必错。爱奇艺的题也有这个风格,喜欢在细节上做文章,一个选项里的小括号位置都会影响整个逻辑。
另外,CSS部分不会直接考“flex怎么用”,而是给一个布局需求,让你写属性,或者给你一段代码,问最终渲染居中还是歪了。这说明考点是“实际场景下能不能正确地使用属性”,不是背单词。而 HTML 语义化、BFC、层叠上下文这些虽然老,但依然会反复出现——因为这些直接决定了页面的可维护性和渲染表现。
1.2 为什么大厂笔试偏爱这类题型
很多人疑惑:前端平时写业务都是 Vue/React,为什么笔试不考框架?核心原因是框架迭代太快,语言和浏览器才是恒定的底层。Vue 3 的源码、React 的调度机制,本质上都是基于 JS 语言特性和浏览器 API 来实现的。如果一个候选人连var和let的区别、==和===的转换规则都说清楚,那让他上手框架也得靠猜。
爱奇艺作为视频巨头,前端团队要面对播放器、首屏渲染、流量控制、广告投放、页面性能优化等高复杂度场景。这些场景非常依赖对事件循环、DOM 操作代价、缓存策略、网络请求调度的深入理解。所以笔试出题方向天然偏向这些基础能力。
同时,笔试题也筛掉“只会调接口写页面”的候选人。前端岗位的竞争早就白热化了,单纯会“调库”“会用组件”已经不够。企业需要一个候选人能解释“为什么页面卡”、能定位“为什么这个按钮点击后延迟反馈”、能设计“如何减少不必要的渲染”。这些全得靠基础功底。所以做这份题,与其说是应付面试,不如说是一次自我体检。
2. 核心细节解析与实操要点
2.1 JavaScript 基础考点:从执行上下文到闭包
JS 部分最核心的几块:执行上下文与作用域链、闭包、this 指向、原型链、Event Loop、ES6+ 新特性。爱奇艺这类大厂笔试很少直接考概念,更多是给代码问输出,所以关键是要学会“模拟执行过程”。
一个常见题型是:
var a = 1; function foo() { console.log(a); var a = 2; } foo();输出是什么?答案是undefined。原因是var声明会提升到函数作用域顶部,所以函数内的a在打印时已经被定义,但还没有赋值。这就叫变量提升。很多人只看答案觉得简单,但换成let就会变成ReferenceError,因为let存在暂时性死区(TDZ)。
闭包题往往结合循环:
for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); }, 100); }输出是3 3 3。原因是var没有块级作用域,setTimeout回调里访问的是同一个i,而循环结束时i已经是 3。把var改成let,或者用 IIFE、bind、闭包包一层,才能输出0 1 2。这背后实际上就是作用域捕获的时机问题,也是笔试里最常设的坑。
关于 this 指向,我给大家一个最实用的判断口诀:“函数被谁调用,this 就指向谁”。但还要记住例外:箭头函数没有自己的 this,它继承外层词法作用域的 this;new调用的时候 this 指向新对象;call/apply/bind可以显式指定 this。笔试题里最常见的套路是:
var obj = { name: 'a', fn: function() { console.log(this.name); } }; var f = obj.fn; f();这段输出undefined(严格模式下)或全局对象属性,而不是'a'。因为f()调用时,调用者是全局对象,不是obj。这类题考察的是“引用丢失”问题,在 React 类组件时代经常出现,现在写函数组件也偶尔会遇到类似场景。
2.2 CSS 与浏览器渲染:盒模型、BFC、层叠上下文
CSS 部分不能只靠“用过 flex”。笔试题会重点考察:标准盒模型和 IE 盒模型的区别、外边距折叠、BFC 的形成条件与作用、层叠上下文、水平垂直居中方案、flex 的 flex-grow/shrink 计算。
盒模型这道基础题,每次笔试出现率都极高。box-sizing: content-box时,width只算内容宽度;border-box时,width包含内容、内边距和边框。实际开发时设置全局* { box-sizing: border-box; },可以减少大量宽高计算麻烦。这一点在笔试题里往往通过“问某个元素的实际占位宽度”来考。
BFC 是一块硬骨头,但记住“BFC 是一个独立的渲染区域,内外互相不影响”就抓住了本质。触发 BFC 的条件很多:float不为none、overflow不为visible、display: inline-block、position: absolute/fixed、display: flex等。用途主要是:清除浮动、防止外边距折叠、阻止元素被浮动元素覆盖。比如经典的“父元素高度塌陷”问题,给父元素设置overflow: hidden触发 BFC,就能把浮动子元素的高度算进去。
层叠上下文则容易和z-index混淆。实际上,z-index只在相同层叠上下文中比较。transform、opacity、filter、position加z-index都会创建层叠上下文。笔试题常给一个多层嵌套的定位元素,问你哪个在最上面,这时候就要按“父级上下文先比,再比同级的 z-index”来推。我见过不少人死记z-index: 9999,碰到嵌套场景还是出错,关键是理解规则。
2.3 浏览器与网络:HTTP 缓存机制
前端笔试里网络部分的常客是:TCP 握手、HTTP 状态码、GET/POST 区别、HTTP 缓存、跨域。爱奇艺这套题里同样有涉及,尤其是缓存。
HTTP 缓存可以简单理解为“服务器给资源打标签,浏览器下次按标签决定是否复用”。强缓存阶段,浏览器直接用本地副本,不请求服务器;协商缓存阶段,浏览器带着标签去问服务器“这个资源还能用吗”,服务器决定返回304还是新资源。相关字段有Cache-Control: max-age、Expires(强缓存),Last-Modified/If-Modified-Since、ETag/If-None-Match(协商缓存)。
前端笔试特别爱问“输入 URL 到页面展示的全过程”。这不是背流程就行,而是要说明每一步的关键机制:DNS 解析(可能会查缓存)、TCP 连接(三次握手)、发送 HTTP 请求(携带缓存校验信息)、服务器返回响应、浏览器解析 HTML 构建 DOM 树、解析 CSS 构建样式规则、JS 阻塞解析、布局与绘制。能流畅说出这个过程,说明你对浏览器运行机制有整体概念,这也是性能优化思路的起点。
2.4 手写代码与算法:从数组去重到防抖节流
手写题是笔试中的重头戏。最常出现的几类:数组去重、数组扁平化、深拷贝、防抖节流、Promise 实现、函数柯里化、Pub/Sub 事件总线。爱奇艺这类题的特点是,要求不一定用 ES6 一行流,而是看你能不能写出健壮且可读的实现。
数组去重看起来简单,但考察的点是你知不知道多种方式:Set一行解决;filter + indexOf解决但只能处理原始类型;Map或对象键值处理引用类型。如果面试官追问“[1, '1']去重后有几个”,你就会发现Set用的是SameValueZero比较,不会把1和'1'混掉。
防抖和节流是面试必考题,也是笔试手写题常客。防抖(debounce)的核心是“只认最后一次”:短时间内连续触发,只有最后一次等待期结束后才执行。典型场景是搜索框输入联想。节流(throttle)的核心是“限制频率”:不管触发多少次,每段时间最多执行一次。典型场景是滚动事件、resize。实现都不难,但要注意this绑定和参数透传,否则改 bug 改到怀疑人生。
下面是一个带this和参数处理的防抖实现:
function debounce(fn, wait) { let timer = null; return function(...args) { const context = this; if (timer) clearTimeout(timer); timer = setTimeout(() => { fn.apply(context, args); }, wait); }; }3. 实操过程与核心环节实现
3.1 数组与对象操作:常用方法背后的原理
笔试题里经常考“下列哪个方法会改变原数组”。很多人用的时候不关心这个,但笔试偏偏要考。像push、pop、shift、unshift、splice、sort、reverse会改变原数组;而map、filter、reduce、slice、concat不会。如果记不清,可以直接用一个技巧:方法名暗示是“原地操作”的,一般会改。
Array.prototype.reduce是高频考点,因为它是“万能函数式工具”。笔试有时候会让你用reduce实现map、filter、groupBy或者compose。比如用reduce实现数组扁平化:
function flatten(arr) { return arr.reduce((acc, cur) => { return acc.concat(Array.isArray(cur) ? flatten(cur) : cur); }, []); }对象相关考点则集中在:属性遍历(for...in 与 Object.keys 的区别)、对象深比较、可选链、解构赋值、展开运算符。有一道经典题是:
const obj = { a: 1 }; const obj2 = { ...obj }; obj2.a = 2; console.log(obj.a); // 1,因为第一层是浅拷贝但如果对象里嵌套了对象,展开运算符只能拷贝第一层引用,修改嵌套对象会连坐。所以深拷贝必须自己写递归或借助structuredClone或lodash.cloneDeep。笔试写深拷贝时,还要考虑循环引用、日期、RegExp、Map、Set 等情况,能写出一个面试官会点头的版本,能加分不少。
3.2 异步流程控制:从回调地狱到 Promise
异步是前端笔试的“分水岭”。一部分人看到 Promise 就头晕,但真正理解事件循环后,会发现所有异步题都是一个套路:同步代码先执行,然后按微任务和宏任务的优先级轮询执行。
你只要记住 Node.js 和浏览器的核心差异不是太大,最重要的是“每执行完一个宏任务,都会清空整个微任务队列”。下面这个顺序题几乎成为大厂必考:
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属于微任务,setTimeout属于宏任务,同步代码结束后会先清空微任务队列,再去取宏任务。
如果题目里再加async/await,其实也简单:await右边的表达式会立即执行,await之后的代码相当于.then里的回调,会放入微任务队列。所以看到async题,先把同步部分推完,再把await后面的内容当作微任务按顺序排好即可。
3.3 网络请求与 API 设计:前端如何和服务器协作
前端笔试还会通过代码题考察你对HTTP 状态码和请求方法语义的理解。比如“创建一个资源用什么方法?更新整个资源用什么方法?删除用什么方法?”——这就是 RESTful 风格。POST用于创建,PUT用于整体更新,PATCH用于部分更新,DELETE用于删除。如果对语义不熟,接口设计时容易和别人撞车。
另外一个高频点是GET 与 POST 的区别。很多人背“GET 有长度限制、POST 安全”,其实不严谨。本质区别更多体现在:GET 请求通常用于“查询”,参数放到 URL 上,会被浏览器缓存、会留在历史记录里;POST 通常用于“提交”,参数放 body,不会被缓存。至于长度限制,其实是服务器或浏览器实现的限制,不是 HTTP 协议硬性规定。这道题考的是你能不能区分“协议语义”和“实现限制”。
还有一类代码补全题,要求你封装一个request函数,处理超时、重试、错误码。实现上核心是“用Promise.race做超时控制”,或者“用递归/循环做重试”。下面是一个简化版的超时请求封装:
function requestWithTimeout(url, options = {}, timeout = 5000) { return Promise.race([ fetch(url, options), new Promise((_, reject) => { setTimeout(() => reject(new Error('timeout')), timeout); }) ]); }这种题看着不难,但能把边界条件想清楚(超时后要中断请求、重试要避免重复提交、loading 状态如何处理),就能体现你写业务代码时是否严谨。
3.4 前端工程化:模块化、构建工具和组件化
爱奇艺这套题既然叫前端开发笔试,工程化相关的内容也免不了。常见方向有:CommonJS 与 ES Module 的区别、Tree Shaking 的原理、webpack 的构建流程、Vue/React 组件通信。
CommonJS 和 ES Module 的区别是基础题,但很多人的理解只停留在“一个是require,一个是import”。深入一点:CommonJS 是同步加载、动态加载、输出的是值的拷贝;ES Module 是静态分析、异步加载、输出的是值的引用。这些差异直接决定了浏览器和 Node.js 对模块的处理方式不同。现在 Node.js 同时支持两种,但.mjs与.cjs后缀不同,行为也不同,笔试有时候会出这个坑。
关于组件化,前端笔试可能给一段 Vue 或 React 代码,问你父子组件如何通信、跨级组件如何通信、状态提升是什么。这类题目的核心是“单向数据流”:父组件通过 props 向子组件传数据,子组件通过事件回调通知父组件更新。如果项目里状态复杂,再用全局状态管理(Vuex/Redux/Zustand)或者 Context/Provide-inject。能把这个逻辑讲清楚,说明你在团队协作中不会乱写全局变量。
4. 常见问题与排查技巧实录
4.1 读代码题总“想当然”怎么办
读代码题是踩坑重灾区。最常见的问题就是“还没理清变量提升,就直接按从上到下的顺序读”。比如这段经典代码:
var name = 'global'; function printName() { console.log(name); var name = 'local'; } printName();如果你按顺序读,会以为先打印global,实际输出undefined。原因前面说过了:函数内var name提升到顶部,但赋值不提升。遇到这种“怎么和我想的不一样”的题,先停下来把代码重写一遍,把所有var声明提到作用域顶部,再模拟执行。
还有一个常见问题是忽略严格模式。如果代码开头有'use strict',那么this在独立函数调用中是undefined,而不是全局对象。变量赋值时不声明会直接报错。很多人在普通模式下测没问题,一旦环境切换就懵。所以做题时先看文件开头有没有严格模式标记,这会改变整个执行结果。
4.2 Promise 题老是排错顺序
异步顺序题出错,通常不是不知道“微任务先执行”,而是没有区分 Promise 构造器中的代码和.then中的代码。新手的误区是以为new Promise里的代码也是异步的。实际上,Promise构造器里的函数是同步执行的,只有resolve或reject之后的.then回调才是微任务。
举个例子:
new Promise((resolve) => { console.log('inside'); resolve(); console.log('after resolve'); }).then(() => { console.log('then'); });输出顺序是inside、after resolve、then。resolve()只是把状态改为 fulfilled,并不会立即执行.then,函数体剩余代码仍然同步执行完。这个细节在笔试题里经常设陷阱,回答的时候不要一看Promise就以为是异步。
4.3 布局题老忘记“父容器尺寸不定”的情况
CSS 笔试题的另一个高频失分点,是忽略父容器没有固定高度/宽度时的表现。比如经典的“让子元素垂直居中”问题,很多人直接写margin: auto,但margin: auto只能在水平方向自动居中,垂直方向需要display: flex或position + transform配合。如果父容器没有设置高度,垂直居中其实没有意义,因为父容器高度由内容撑开。
再比如flex: 1的含意,很多人只知道“填满剩余空间”,但不清楚它其实是flex-grow: 1; flex-shrink: 1; flex-basis: 0%的简写。笔试如果给你一个flex: 1和flex: auto的对比,你要能解释区别:flex: 1的 basis 是 0,所以不会受内容固有尺寸影响;flex: auto的 basis 是auto,所以会优先基于内容尺寸分配空间。这一类细节,平时写样式不注意,做题必丢分。
4.4 手写题“会写但没考虑边界”导致扣分
手写代码题最容易出现“主流程对,边界漏”的情况。比如写深拷贝,只处理了普通对象和数组,没处理null、Date、RegExp、循环引用,就会被扣分。实际上,面试官看手写题,看的不是你“能不能写出来”,而是你“能不能考虑全面”。
我建议你面试前准备一个“模板库”,把高频手写题都写成“带边界检查”的版本,而不是网上抄个一行版就背。比如数组去重,可以按“原始类型 + 引用类型 + NaN”这三个维度来写;Promise.all要确保“有一个 reject 就整体 reject”,还要保证“传入空数组时返回一个已解决的空数组”。这些边界条件,恰恰是日常写代码中最容易忽略的,也是区分资深者和初学者的关键。
5. 深度扩展:这份笔试题的深层价值
5.1 从笔试看大厂前端的能力模型
把爱奇艺笔试题放到更大的视角下看,它其实描绘了“大厂前端的能力模型”:语言基础扎实、运行机制清楚、工程化意识强、边界意识好。这不是爱奇艺一家的要求,而是整个行业对成熟前端工程师的通用预期。
很多从业三年左右的前端,工作里一直在写业务组件,遇到问题就百度、Ctrl+C/V,看起来什么都会,但笔试一考就露馅。原因不是不聪明,而是没有建立“原理到实践”的映射关系。比如用了无数次Array.prototype.map,却答不出它和forEach的本质区别;用了无数次display: flex,却不清楚 Flex 容器的三条主轴交叉轴规则。这份笔试题就像一面镜子,能照出你基础功底的薄弱环节。
另外,这套题对“学习路径”也有指导意义。如果你现在还是学生或者刚入行,与其追热点学新框架,不如先把 JS 高程、CSS 权威指南、HTTP 权威指南这些“老书”啃透。框架更新换代是常态,而语言特性和浏览器机制是十年内都不会大变的地基。地基稳了,学框架只是换个语法而已。
5.2 针对不同阶段的备考策略
对于在校生和应届生,笔试准备的核心是“刷题 + 建错题本”。不要只闷头在牛客上刷题,每次做错的题,都要把“错误原因、正确推导、涉及知识点”记下来。过了两周再拿出来做一遍,直到能独立推导出正确答案为止。针对爱奇艺这套题,你可以关注高频考点,但不要跳过你认为“简单”的部分,因为笔试的坑往往就埋在简单题里。
对于已经工作的前端,备考重点应该放在“从原理角度重新组织答案”上。比如问你“什么是闭包”,不要只回答“函数内部访问外部变量”,而要能画出作用域链、说出内存回收的影响、举一个实际业务场景(比如防抖、柯里化、组件内部函数)。这样你在面试官追问时才能接得住。笔试是笔试,面试是面试,但能力模型是通的,能把面试中讲清楚的东西写到纸面上,才说明真的掌握了。
5.3 我发现的高频规律与出题趋势
如果把近年的大厂前端笔试题放在一起看,会发现出题趋势非常明显:越来越重视“真正的场景题”,而不再满足于八股概念。所谓场景题,就是“给一个具体业务问题,让你设计实现方案”。比如“有一个搜索框,用户输入后需要防抖搜索,同时要处理竞态条件(快速输入时只取最后一次结果),你会怎么做?”这类题考察的点已经超越了语言语法,进入架构设计和边界思考层面。
爱奇艺这套2019年笔试题,虽然整体偏基础,但已经能看到这种趋势的苗头。比如它会在代码题里设置“如果某个请求失败了怎么办”“如果这里传的参数是不确定的怎么办”等铺陈,其实就是在考察边界处理。现在的笔试题更是直接甩场景:实现一个带并发限制的异步调度器、实现一个 LazyMan、实现一个带超时的 fetch、实现一个可取消的 Promise。这些题目背后,考察的都是抽象能力和工程思维。
我个人判断,未来前端笔试的难度还会继续上探,但考点永远围绕“语言机制 + 异步控制 + 工程化 + 性能优化”这几条主线,数据结构与算法也在逐渐加重。所以趁早补齐基础,比临时抱佛脚刷题靠谱太多。
5.4 心态调整:笔试不是终点而是起点
我见过不少候选人,因为一套笔试题做崩了就直接放弃,这其实挺可惜的。笔试做不好,往往不是能力不行,而是练习方式不对或者准备周期不够。你只要把笔试当作一次学习诊断,把自己不会的题整理成专题,一个个攻破,效果远好于焦虑“我怎么这么菜”。
另外提醒一句:不要迷信“原题”。网上流传的题库再多,也不可能押中所有面试题。真正稳妥的做法是,把每道题背后的知识点当成一个“知识锚点”,从这个锚点延伸出去,把相关的概念、原理、应用场景全部复习一遍。比如遇到一道“改变函数 this 指向”的题,你可以复习call/apply/bind的区别、手写bind、箭头函数、构造函数new的过程、React 类组件中 this 绑定的常见错误。这样一来,一道题就变成了一套知识网络,不管怎么变着问,你都能应对。