ARTICLE DETAIL

资讯详情

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

第四范式前端笔试复盘:JavaScript、浏览器与手写代码核心考点解析

第四范式前端笔试复盘:JavaScript、浏览器与手写代码核心考点解析 最近在翻旧资料的时候看到当年准备秋招时整理的一套第四范式前端笔试复盘笔记感慨还挺多的。2020年那会儿AI四小龙的招聘热度正高第四范式的笔试题目在技术社区里讨论度也不低。现在回头看这套题虽然已经过去几年但考察的知识点依然很经典——JavaScript基础、浏览器原理、框架实现思路、手写代码能力基本覆盖了前端面试的核心疆域。今天就把我当时对这套题的拆解和复盘整理出来希望能给正在准备大厂前端岗位、尤其是AI公司前端岗位的朋友一些参考。先说个整体感受第四范式的笔试题量和难度属于中等偏上但更看重基础扎实度和代码实现能力题目设计得比较讲究不是靠背题就能过的。这份复盘完全是基于我个人的回忆和理解题目描述可能会有细节出入但考察点和解题思路是通用的值得好好研究。1. 这份笔试到底考什么第四范式前端的出题风格1.1 先聊两句这家公司和它的技术调性第四范式是做AI平台和机器学习核心技术的公司技术社区里对它的普遍认知是“工程师文化浓厚算法功底扎实”。这种技术背景会直接影响前端团队的招聘偏好——他们招前端不完全只看你能不能写页面更在意你写代码的底层逻辑是否清晰、遇到复杂问题时能不能有体系化思考。这套笔试的题目设置也印证了这一点。选择题和简答题里JavaScript的各种底层机制占了很大比重编程题更是直接对手写代码能力提出硬性要求。由于AI公司的业务通常涉及大规模数据处理和复杂交互场景前端需要处理的数据结构、异步流程、性能优化需求都比普通业务团队更多所以这些方向顺理成章成了考察重点。1.2 从题型分布看考察重心从我回忆的情况来看整套卷子大致分为四个模块计算机基础与网络、JavaScript核心机制、框架与工程化、在线编程题。每个模块的题量和考察方向可以整理成下面这个表格模块常见题型侧重点网络与浏览器选择题、简答题缓存策略、渲染流程、跨域方案JavaScript核心选择题、代码输出题数据类型、作用域、事件循环、异步框架与工程化简答题MVVM原理、虚拟DOM、组件通信、webpack算法与手写在线编程数组处理、防抖节流、深拷贝、Promise这个分布透露出的信息很明确业务开发中经常会用到的能力被反复考察说明面试官不是想刁难人而是想筛出真正能干活、能解决实际问题的候选人。如果你平时开发只停留在“调接口、绑数据、写样式”的层面这套卷子做起来会很吃力如果你对底层原理有过系统研究做起来就会顺手很多。2. 基础题回顾网络、缓存与浏览器原理2.1 从输入URL到页面展示怎么答才不丢分这类题在各大厂笔试里都是常客第四范式也考了不过考察角度稍微刁钻一些。直接让你“描述从输入URL到页面展示的过程”是最基础的版本但第四范式的题目会在其中穿插一些追问比如“在哪个环节可能发生DNS缓存命中”“渲染过程中JavaScript的加载和执行会阻塞什么”。如果只是机械地背答案很容易在追问环节卡壳。我从实际操作出发的建议是别按一条线从头背到尾而是按阶段拆开回答。网络请求阶段要说清楚DNS解析、TCP握手、HTTP请求发送浏览器解析阶段要区分HTML、CSS、JavaScript三种资源的处理顺序渲染阶段要讲清楚DOM树、CSSOM树、渲染树、布局和绘制的流程。最后一定要提一嘴“JavaScript会阻塞解析所以才有defer和async”这一句往往能切中考察者的意图。我自己的答题经验是先按阶段说出主干流程再说每阶段的细节和可能的性能瓶颈。这样显得有框架感也有深度面试官听起来会觉得你是真懂而不是背知识点。2.2 浏览器缓存策略的考察要点缓存题几乎是前端笔试的必备菜第四范式的题目主要考察两个维度一是强缓存和协商缓存的区别二是实际项目里的应用场景。强缓存的核心是Cache-Control和Expires其中Cache-Control的max-age指定了缓存有效期Expires是绝对时间优先级低于Cache-Control。协商缓存则依赖Last-Modified/If-Modified-Since和ETag/If-None-Match两对头信息。这里有一个容易混淆的点Last-Modified只能精确到秒如果文件在同一个秒级时间内被修改客户端会拿到错误缓存ETag则可以更精确地判断文件内容是否变化所以像Webpack这样的构建工具生成的静态资源文件名里都会带上hash值再配合Cache-Control: max-age31536000实现长缓存。我从实战角度补充一点如果在面试题里碰到“为什么刷新页面后请求状态码是304”这类问题回答时要说清楚强缓存和协商缓存的触发顺序——浏览器先判断强缓存是否命中缓存有效就直接用本地副本缓存失效后再发请求带上If-None-Match或If-Modified-Since头让服务器判断是否需要重新返回完整内容。这个顺序很容易被搞混建议用表格对比记忆。2.3 跨域问题答题得从“为什么”说起跨域几乎是我遇到的每一场前端笔试都会出现的考点。第四范式的题目不是简单问“怎么解决跨域”而是先问你“浏览器为什么要有同源策略”然后再让你写解决方案。这两问是递进关系。同源策略保护的其实是用户数据安全防止恶意网站读取另一个网站的Cookie或接口数据。没有这一层限制你登录过购物网站后访问一个恶意页面你的登录态就可能被窃取。理解了这一层答案就不难组织出来。解决方案常见的就那么几种JSONP、CORS、代理转发、postMessage。不过很多人在写CORS的时候只写出Access-Control-Allow-Origin一个响应头这其实不够。实际开发里还要处理预检请求OPTIONS、允许的请求方法、允许的请求头、是否携带凭证等配置。我建议回答CORS时把响应头完整地列出来再解释简单请求和预检请求的区别这样能跟只会背概念的人拉开差距。JSONP虽然现在用得少了但它的原理——动态创建script标签、利用src不受同源策略限制——还是值得写清楚。我印象里版本比较旧但考察点至今仍然是高频。如果你现在准备面试建议把跨域相关的实现都动手写一遍尤其是CORS的几种配置方式和代理转发的具体配置代码笔试里很容易让你“写出关键配置代码”。3. JavaScript核心考点这些题最拉差距3.1 数据类型的边界题typeof、instanceof到底怎么用第四范式的选择题里有一道这样的题给出一段代码判断typeof各种值的输出结果。这类题表面简单但坑非常多。typeof null返回object这是JavaScript历史遗留的bug但几乎每次笔试都会考typeof function(){}返回function这是typeof唯一返回function的引用类型typeof NaN返回number因为NaN确实是Number类型还有typeof undeclaredVariable不会报错而是返回undefined。instanceof的考察点则是它的判断原理——沿着原型链查找看左侧对象的原型链上是否有右侧构造函数的prototype。这里值得多说一句instanceof的右侧必须是一个对象类型函数如果写成基本类型会直接报错它也不能跨iframe判断因为不同iframe有各自独立的全局对象。这些边界场景往往是选择题里用来拉开差距的。我当时的复习方法是把数据类型检测的全部方法做了一个横向对比表逐一记住它们的使用场景typeof适合判断基本类型instanceof适合判断引用类型的具体构造函数Object.prototype.toString.call()可以准确判断所有内置类型Array.isArray()专门判断数组。笔试碰到“怎么判断一个变量是不是数组”这类题时把几种方法对比着回答深度立刻就有了。3.2 事件循环微任务宏任务的执行顺序这是第四范式笔试里我认为最拉分的一道选择题。题目大概长这样给出一段混合了setTimeout、Promise、async/await的代码让你写出最终的输出顺序。这类题考察的就是事件循环机制。先理清底层逻辑JavaScript是单线程的但通过事件循环实现异步非阻塞。执行栈同步代码时遇到异步任务会先挂起等时间到了或Promise状态变化了再回调。回调分两类——微任务Promise.then、MutationObserver、queueMicrotask和宏任务setTimeout、setInterval、I/O、UI渲染。事件循环的每一轮会先清空宏任务队列里的一个任务然后清空整个微任务队列再进入下一轮。这道题的易错点主要集中在Promise构造函数里的代码是同步执行的.then()回调才是微任务async函数里await后面的代码会被包成微任务执行。很多人会把构造函数里的同步代码误认为是异步的一上来就排错顺序整道题就全错了。我建议准备这类题时不只是背结论要自己画执行顺序图把每一行代码的执行时机标出来。我当时把所有常考的异步代码组合都手动跑了一遍然后在脑内模拟输出过程选择题基本就稳了。3.3 闭包与作用域的经典陷阱闭包题在第四范式的笔试里属于必考题基本是以“代码输出题”的形式出现。最典型的一道是循环中var声明和定时器结合的题for (var i 0; i 5; i) { setTimeout(function() { console.log(i); }, 1000); }输出结果是5个5而不是0、1、2、3、4。原因很简单var没有块级作用域i是全局变量等定时器回调执行的时候循环早就跑完了i已经变成了5。解法有三种ES6的let声明、立即执行函数包裹、或者Function.prototype.bind传参。但笔试往往不光要你写解法还会追问闭包的形成原因和内存影响。我记得第四范式有一道简答题就是“谈谈闭包的优缺点”如果你只写“可以让外部访问函数内部变量”得分一定不高。完整的回答应该包括闭包的形成条件函数嵌套、内部函数引用外部变量、能实现的功能数据私有化、延迟执行、函数柯里化、必须注意的问题内存泄漏风险——被闭包引用的变量不会被垃圾回收所以在不需要时要手动置空引用。闭包的实际应用在笔试题里经常以“防抖”“节流”“柯里化”的形式出现这些手写题的核心就是闭包保存状态。所以闭包不是孤立的知识点而是串联整个JavaScript进阶能力的关键。我建议先把闭包的底层机制彻底搞明白再去做防抖、节流、柯里化的手写题思路会清晰很多。3.4 手写call/apply/bind考察原型与this绑定第四范式的笔试题里有一道手写Function.prototype.bind的编程题这在当年算是比较经典的考察方式。这道题考察的点有两个一是是否理解bind的实现原理二是对边界情况的处理能力。bind的核心实现思路不复杂返回一个新函数调用时把原函数作为method挂到指定this对象上执行。但真正写起来有几个坑首先要区分是当作构造函数调用new还是普通函数调用bind返回的函数如果被new了this应该是新创建的对象而不是传入的bind对象其次要考虑原函数本身的length属性是否要保留还有就是要处理参数拼接——bind传入的参数和调用返回函数时传入的参数要合并。这道题给我的启发是笔试里的手写题不是看你背了多少API而是看你能不能从底层重新实现出来。如果你平时只是用bind没想过它内部怎么工作这张卷子就会卡住。我当时做完这道题之后把call、apply、bind三者的实现都写了一遍再结合new的优先级去理解this绑定规则整个JavaScript的函数知识体系就通顺了很多。4. 框架与工程化Vue源码级理解题4.1 Vue响应式原理为什么值得写满一屏第四范式在框架层面更偏向Vue考察方式也是“源码理解”级别的。最常见的题目就是请描述Vue 2.x响应式原理。答案的核心是Object.defineProperty。Vue在初始化时会遍历data中的每个属性通过Object.defineProperty为它们设置getter和setter。getter里做依赖收集——记录谁在用这个数据也就是Watchersetter里做派发更新——数据变化时通知Watcher更新视图。每个组件实例对应一个Watcher当数据变化触发setter时会通知Watcher去执行更新函数最终重新渲染组件。这里容易忽略的细节是Vue 2的响应式在对象新增属性和删除属性时是检测不到的所以才有Vue.set和this.$set这两个API。数组的响应式也是通过改写数组的七个方法push、pop、shift、unshift、splice、sort、reverse来实现的。Vue 3把Object.defineProperty换成了Proxy好处显而易见可以代理整个对象而不需要遍历每个属性新增和删除属性也能监听到性能更好。如果笔试里问“Vue 3为什么用Proxy”要答出Proxy的代理能力、性能提升、以及解决了Vue 2的哪些限制这三个层面。这道题虽然看起来偏原理但实际开发中遇到的莫名其妙不更新问题十有八九都是响应式机制没弄明白。4.2 虚拟DOM和diff算法的核心逻辑除了响应式虚拟DOM也是第四范式简答的高频题。题目一般会问虚拟DOM是什么为什么需要它diff算法是怎么工作的虚拟DOM本质就是一个描述真实DOM结构的JavaScript对象它有type标签名、props属性、children子节点这些字段。有了虚拟DOM框架可以在内存中用计算速度换取真实DOM的操作次数——因为操作内存中的对象远比操作真实DOM性能好。diff算法的核心逻辑可以用三个词概括同层比较、key优化、最小化更新。同层比较指的是只对比同一层级的节点不跨层级移动key则用来标记列表中每个节点的身份方便复用而不是重新创建。所以v-for的时候如果不写key或者乱用index会导致节点复用错乱状态出问题。这道题如果能结合源码细节来回答会更出彩比如比较新旧VNode时判断sameVNode的条件、patch过程中什么时候复用节点、什么时候直接替换整棵子树。我当时看了一遍Vue 2的patch源码再去做这道简答题明显感觉内容饱满了很多而不是干巴巴地背那几条结论。4.3 组件通信方案父子、兄弟、跨层怎么选第四范式的框架题里还出现过一道相对实际的题组件A和组件B是兄弟组件需要共享数据应该怎么做这道题没有标准唯一解关键是看你能列出多少种方案并说出各自的适用场景。父子通信可以用props和$emit兄弟通信可以借助共同的父组件做中转或者用事件总线Vue 2中用空的Vue实例做$on/$emit跨层级的通信可以用provide/inject但它是非响应式的更复杂的场景就要上Vuex或者Pinia这样的全局状态管理库。我在复盘的时候发现面试官出这道题其实是想看候选人对工程方案的理解——你平时在项目里遇到类似需求能不能做出合理的技术选型。如果只会写业务代码可能就会给出“用一个全局变量存”这种土匪方案。真正合理的回答路径应该是先分析数据量大小和共享范围然后给出最小成本的通信方案。这也是笔试和面试的共通点考察的不只是你会不会而是你怎么做决策。4.4 webpack的关键配置与构建优化工程化部分第四范式考过webpack相关的问题主要围绕Loader和Plugin的区别、构建优化手段来展开。Loader是webpack里用来处理文件转换的它是一个函数接收文件内容作为参数并返回转换后的内容常见的有babel-loader、css-loader、style-loader、file-loader等。Plugin则是用来处理整个构建过程中的任务比如打包优化、资源管理、环境变量注入它的能力比Loader更宏观。构建优化的答题方向一般有这几个层次代码体积层面Tree Shaking、代码分割、压缩混淆构建速度层面缓存Loader执行结果、多进程构建thread-loader、使用DLLPlugin预编译资源层面图片压缩、CDN引入第三方库、polyfill按需加载。实际开发中最常用的是splitChunks把公共依赖拆出来以及路由懒加载让首屏少加载资源。这类题的答案网上有很多整理好的版本但记忆难度比较大。我的经验是找一个自己真实做过的优化案例把优化前后的对比数据记下来回答的时候用自己的项目去说明方案可信度和深度都会高很多。5. 编程题实战手写代码的答题策略5.1 手写防抖节流如何答出工程化思维第四范式的在线编程题里有一道防抖debounce的实现这是前端手写代码题里最经典的一道也是我能明确回忆起来的一道题目。题目一般会要求“实现一个防抖函数并说明应用场景”。防抖的核心是事件触发后延迟一段时间执行如果在这段时间内再次触发就重新计时。所以返回函数内部要清理之前的定时器。标准实现大概这样function debounce(fn, delay) { let timer null; return function(...args) { const context this; clearTimeout(timer); timer setTimeout(() { fn.apply(context, args); }, delay); }; }这里有两个关键细节容易丢分一是this要指向正确的调用对象所以要用fn.apply(context, args)而不是直接fn(...args)二是要支持传参所以要用rest参数...args把事件对象等参数透传进去。应用场景这个问法其实是加分项回答得好能体现工程经验。最常见的是搜索框输入联想——用户输入过程中连续触发input事件如果不做防抖每按一个键就会发一次请求后端压力大前端还容易收到乱序的响应。防抖后用户停止输入一段时间再触发请求体验和性能都更好。类似的还有窗口resize事件、滚动事件配合防抖节流的权衡。我当时在答题时顺便写了一版带immediate参数是否立即执行一次、带取消方法的增强版还解释了在Vue组件里使用防抖时要注意在beforeUnmount里取消防抖定时器以防内存泄漏这个细节后来复盘时觉得非常加分。节流throttle和防抖容易搞混我的记忆方法是防抖是“你看完电影再关门”节流是“每隔一段时间必须开门一次”。5.2 深拷贝一题测出你的边界处理能力深拷贝题比防抖更考验代码的完备性。这类题的好答之处在于面试官往往会在题目后面标注“注意处理循环引用和Symbol属性”等提示。最简单的深拷贝一行代码就能写JSON.parse(JSON.stringify(obj))但这几个场景会挂包含undefined、函数、Symbol时这些属性会被直接丢弃包含Date、RegExp、Map、Set时会被转成普通对象或字符串包含循环引用时会直接报错。所以真正的深拷贝需要用递归加WeakMap来解决循环引用用Reflect.ownKeys来遍历所有属性包括Symbol和不可枚举属性。我当时给出的版本核心逻辑是先判断是不是引用类型不是就直接返回然后用WeakMap记录已拷贝的对象遇到循环引用时直接返回缓存结果接着用Object.create(Object.getPrototypeOf(target))保留原型链最后区分普通对象、数组、Date、RegExp、Map、Set分别处理。这类题想拿高分的关键是考虑周全而不是写完递归就结束。我在复盘时把边界场景整理成了一个小表格建议你也这样做边界情况JSON方案的表现完善方案的处理undefined、函数、Symbol属性被丢弃保留原值Date、RegExp变成普通对象重建对应类型Map、Set变成空对象逐一拷贝条目循环引用直接报错WeakMap缓存原型链丢失原型保留prototype5.3 数组扁平化与手写Promise热身高频题除了上面两道相对进阶的题第四范式的笔试里也考了比较基础的数组处理和异步流程控制题。数组扁平化的实现方式很多我习惯把它拆成不同难度逐层递进最简单的是arr.flat(Infinity)一行搞定中等难度是用reduce配合concat递归实现再难一点就是自己用栈模拟展开。笔试里如果你直接写arr.flat(Infinity)也不能说错但面试官其实期待看到你能手工实现一遍。这里推荐一个技巧先用递归实现然后追问“如果数组非常深递归会有什么问题”顺势补充迭代实现的方案展示你考虑了性能问题。手写Promise的题目在2020年的前端笔试里已经不算新鲜了第四范式考的大概率是简化版——实现Promise的基本状态流转、resolve/reject和then方法的链式调用。核心就是状态只能从pending变成fulfilled或rejected不能逆向变化then里注册的回调要等状态变化后再执行链式调用时then的返回值决定下一个then的输入。这道题如果从零开始写全非常耗时我当时的策略是先搭好骨架确保状态流转逻辑正确再补then的处理逻辑最后处理值的透传不必追求完整的resolvePromise流程但状态机的正确性是必须保证的。5.4 编程题的答题顺序与时间分配建议这块虽然不是知识点但我觉得每个经历过笔试的人都会有自己的血泪教训。第四范式的线上笔试时间不算特别宽裕我当时按照题量和难度预估了一下时间配比选择题和简答题控制在45%左右编程题留55%左右其中深拷贝和手写Promise这种代码量大的题至少要留出30%的时间。一个我自己踩过坑后总结出的经验遇到编程题不要立刻敲代码先把思路写成注释或伪代码。这样做有两个好处一是帮自己理清逻辑二是即使代码没写完面试官看你注释也能看出你的解题思路有时候能给过程分。另外在线笔试的IDE通常没有本地那么智能各种方法名容易拼错写完一定留时间自己跑一遍测试用例至少覆盖题目给的示例输入。有一个明确的教训是代码写完没测试就提交很可能因为一个拼写错误整题零分太可惜了。6. 复盘与提升前端面试的备考建议6.1 不同基础阶段的准备重点准备这类笔试的时候最忌讳的就是盲目刷题。我先说个真实的体会如果你对题目背后的原理不清楚做一百道题也只会一套就忘一套但如果你把知识体系建起来题目再怎么变都能应对。如果你是刚入门不久建议先把JavaScript核心基础打牢尤其是作用域、闭包、this指向、异步机制这四块这是所有前端面试里最基础也最关键的底层能力。然后是浏览器原理缓存、渲染、跨域、事件循环这些都是必考板块要整体过一遍。如果你已经做了两年业务开发但没系统看过原理重点可以放在框架源码理解和工程化实践上认真搞清楚Vue或React的数据驱动流程、虚拟DOM、diff算法以及webpack构建链路。你平时写业务时遇到的有意思的问题反而可以成为面试里的加分项比如“为什么列表更新后页面没变”“为什么组件在某种情况下不重新渲染”这些真实案例比背概念更能打动面试官。如果你的基础已经比较扎实建议重点打磨手写代码能力不但要能写出来还要能说出设计思路和边界情况处理。这部分是最能体现代码功底的地方也是拉开分数差距的关键。6.2 刷题之外值得长期打磨的前端基本功我在复盘第四范式这套笔试题的时候发现一个现象很多题之所以做不好不是因为这些题本身很难而是因为平时开发时缺少思考的习惯。比如调一个接口时就只是调接口没想过同源策略、CORS、预检请求写一个组件时就只是写模板没想过Vue的数据流是怎么走通的。所以笔试准备不能只看面试题更要借这个机会把前端知识体系补齐。我建议你把面试题当成一个地图每个考点都标注出你自己的掌握程度——熟悉的、模糊的、完全不会的——然后逐项去补。补的时候尽量追到源码层面比如Vue的响应式光知道Object.defineProperty是不够的可以花一下午把相关的源码跟一遍。这个过程虽然费时但收益是长久的。还有一点很重要实际动手做项目。笔试前前后后刷题很重要但刷题刷得再多也不如把一个真实项目从头到尾做好带给你的成长多。你可以刻意在项目里使用组件化开发、状态管理、性能优化手段自己给自己提需求。我见过很多候选人笔试题能做高分但问起项目经验时候说不出什么亮点这样往往也拿不到最终的offer。两者兼顾才能稳扎稳打。我整理这套复盘的时候印象最深的不是某道题怎么解而是当时自己为了准备面试把过去写业务代码时一直模糊的知识点一个个补上了。这种“看懂底层”的感觉才是准备校招笔试最大的收获。希望这份复盘能帮你少走一些弯路也能让你在准备过程中找到属于自己的学习节奏。
返回列表