ARTICLE DETAIL

资讯详情

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

前端面试八股文入门:JavaScript核心机制与浏览器原理

前端面试八股文入门:JavaScript核心机制与浏览器原理 那年杏花微雨我还在第一家公司做前端简历上写着“熟练掌握Vue”面试官问我“Vue的响应式原理是什么”我支支吾吾说了句“就是那个…数据变了页面就变了”然后就没有然后了。后来我刷了三个月八股文才发现当年那些让我栽跟头的问题其实是有规律可循的。这个系列就是想把这些规律整理出来用连续剧的方式一集一集地把前端面试八股文讲透。这一集是“初相遇”也就是整个知识地图的开篇。我们不急着背题目先搞清楚前端面试到底在考什么、八股文在整个面试中占什么位置、哪些知识点是必须拿下的地基然后我会挑几个最核心的基础问题从面试官视角拆给你看。适合准备校招、社招跳槽、以及想系统梳理前端基础的同学。1. 前端八股文到底在考什么1.1 面试官不是要你背书是要你证明“能干活”很多人一提八股文就皱眉觉得面试官就是在刁难人。我跟不少面试官聊过也在面试官的位子上坐过说实话大家平时写业务代码都忙得要死没有谁闲到专门出题为难你。问八股文的真正目的是快速筛掉那些简历写得天花乱坠、实际一问三不知的候选人。你想啊简历上写着“精通JavaScript”那面试官总得验证一下吧。怎么验证最快的方式就是问几个概念性的问题看你能不能讲清楚。如果你连“闭包是什么”都说不上来那“精通”两个字怎么让人信服所以说八股文本质上是一道道“质量抽检题”不是考察你背诵能力而是考察你有没有真正理解这门技术。但这里有个关键区别背书能过的是初级面试理解后才能过中高级面试。同一个问题“闭包是什么”和“闭包在实际项目里用来做什么、有什么坑”是两个完全不同的考察维度。后者才是真正拉开差距的地方。所以这个系列里每道题我都会带着场景讲不会只给你一个干巴巴的定义。1.2 前端面试知识地图从地基到框架前端面试的知识体系大致可以分成七个区块我按考察频率和重要性排个序JavaScript核心机制原型链、闭包、作用域、this、事件循环浏览器工作原理渲染过程、回流重绘、缓存、存储HTML与CSS基础语义化、盒模型、布局方案、响应式网络基础HTTP协议、HTTPS、TCP三次握手、跨域框架原理Vue或React的响应式、虚拟DOM、生命周期工程化模块化、打包工具、代码规范、部署流程数据结构和算法高频手写题、复杂度分析为什么把JavaScript和浏览器放在最前面因为这是前端的地基。框架年年换今天Vue明天React后天可能又冒出来一个新东西但JavaScript和浏览器的底层逻辑不会大变。你把这层打牢了学任何框架都快反过来只学框架不抠基础框架一升级你就慌。这个系列会按这个地图一路展开。第一集“初相遇”先聊聊地图的前两站JavaScript核心机制和浏览器工作原理这两块是大多数公司一面必考的内容先把它们啃下来后面的路就好走了。1.3 “连续剧”的追剧方式别跳集每集都要练既然叫连续剧那就有追剧的讲究。我的建议是第一遍不要跳着看按顺序把每一集的知识点过一遍不求记住全部细节先建立整体认知。第二遍再针对自己薄弱的模块重点刷配合手写代码和项目复盘。每一集后面我都会留一个“自查清单”你可以在看完之后合上文章试试自己能不能把每个问题讲出来。能讲出来不等于能扛住追问所以最好找个人模拟面试或者打开手机录音自己讲一遍回放听听哪里卡壳了。这个方法很笨但实测下来比单纯看有效得多。另外说一句八股文只是面试的一部分现在已经很少有公司只问八股不问项目了。正确的心态是八股文用来证明你的理论基础没问题项目用来证明你确实干过活。两者缺一不可别本末倒置。2. 初相遇必考基础JavaScript核心机制2.1 原型与原型链原型链是JavaScript里最基础也最容易被问懵的一个概念。很多人的理解停留在“每个对象都有__proto__每个函数都有prototype”但真要画张图把实例、构造函数、Object.prototype之间的关系理清楚就露馅了。我给你们一个好记的理解方式JavaScript里的继承本质上是“对象之间的委托关系”。当你访问一个对象的属性时它先在自己身上找找不到就去__proto__指向的对象上找再找不到就继续往上直到Object.prototype再往上就是null了。这条链就是原型链。面试时经常让你手写实现一个new其实考的就是原型链function myNew(Constructor, ...args) { // 创建一个新对象把它的原型指向构造函数的 prototype const obj Object.create(Constructor.prototype); // 执行构造函数把 this 绑定到新对象上 const result Constructor.apply(obj, args); // 如果构造函数显式返回了对象就返回那个对象否则返回新创建的对象 return result instanceof Object ? result : obj; }这段代码里的Object.create(Constructor.prototype)就是关键它把新对象的原型挂到了构造函数的prototype上从而让实例能沿着原型链访问到构造函数原型上定义的方法。手写的时候记得处理构造函数返回对象的情况这是个经典的加分点。追问的时候面试官可能会问“Function.prototype和Object.prototype谁在上层”这类深水区问题。这里有个规律所有函数都是Function的实例所有对象最终都指向Object.prototype而Function.prototype本身也是一个对象所以它的__proto__指向Object.prototype。这条关系链建议自己画一遍画明白了原型链这关就过了。2.2 闭包从原理到内存泄漏闭包的定义很简单函数能够访问它定义时所在作用域里的变量。但每次面试都能刷掉一批人原因在于很多人只会背定义不会讲场景。我常用一个生活化的类比解释闭包——就像是随身带了一个背包。函数本来执行完就该把局部变量丢掉但如果它返回了一个内部函数而这个内部函数还“背着”外部函数的变量那这些变量就不能被回收跟着内部函数一起存活下来。面试里最常见的问题是“用闭包实现一个计数器”function createCounter() { let count 0; return function () { count; return count; }; } const counter createCounter(); console.log(counter()); // 1 console.log(counter()); // 2这个例子里的count变量正常情况下在createCounter执行完就该被回收了。但因为返回的函数还引用着它所以它一直存在。这就是闭包的核心特征外部函数执行完内部函数仍然持有外部作用域的变量引用。进阶问题是闭包和内存泄漏的关系。闭包本身不一定会造成内存泄漏但如果闭包长期持有一个大对象或者被意外地挂到了全局/事件监听器上就会导致这部分内存无法释放。比如在Vue组件里用addEventListener绑定了回调组件销毁时忘了移除监听回调里的闭包引用就一直存在这就是典型的泄漏场景。答题时加上这个例子面试官会觉得你是真踩过坑的。2.3 this 指向四种绑定规则全拆解this指向是前端面试八股文里最经典的“翻车点”之一。很多人靠死记硬背“箭头函数没有自己的this”但换个问法就懵了。实际上this的指向完全由调用方式决定跟你函数定义在哪里无关。整理成四个规则按优先级从高到低new绑定用new调用构造函数this指向新创建的对象显式绑定call、apply、bind可以手动指定this隐式绑定作为对象的方法调用this指向这个对象默认绑定独立函数调用非严格模式下this指向window浏览器中严格模式下是undefined箭头函数不在这四个规则里它不绑定this而是继承外层作用域的this。这道题的关键是当你写一个箭头函数时它的this在定义时就确定了之后无论怎么调用都不会变。面试官很爱出一道综合题让你判断下面这段代码的输出const obj { name: obj, getName() { return this.name; }, }; const fn obj.getName; console.log(fn()); // 输出什么答案是undefined严格模式下会直接报错。因为fn被赋值给一个变量后再独立调用这时候是默认绑定规则this指向的是window非严格模式而window.name通常是不存在的。很多人看到obj.getName就以为是obj其实关键在于调用时的形式obj.getName()和const fn obj.getName; fn()是完全不同的两种调用方式。这个点搞明白了this这块基本就稳了。2.4 事件循环微任务和宏任务的执行顺序事件循环是现在前端面试的必考题尤其是考察你对setTimeout、Promise、async/await的理解程度。很多人在浏览器里写代码没问题但一让排执行顺序就脑袋冒汗。你只需要记住两条铁律同步代码先执行遇到异步就挂到对应的队列里每次事件循环先清空微任务队列再取一个宏任务执行然后再清空微任务队列宏任务包括setTimeout、setInterval、setImmediate、I/O 操作微任务包括Promise.then、MutationObserver以及async/await中的后续代码。来看一道经典题console.log(script start); setTimeout(() { console.log(setTimeout); }, 0); Promise.resolve().then(() { console.log(promise1); }).then(() { console.log(promise2); }); console.log(script end);输出顺序是script start→script end→promise1→promise2→setTimeout。为什么promise1在setTimeout之前因为setTimeout是宏任务即使延迟是0也要等当前宏任务执行完、微任务队列清空后才会被取出执行。再提醒一个容易踩坑的地方async/await里的await后面部分等同于.then()里的回调属于微任务。有时候面试官故意出个await和Promise.then混在一起的题你只要记住“等号右边先同步执行await后面的代码异步执行”这个原则基本不会错。3. 浏览器工作原理从输入URL到页面渲染3.1 一次完整的URL请求发生了什么这是前端面试里出镜率最高的一道综合题。它好考的地方在于一个题目能串起网络、浏览器、渲染引擎等多个知识点。面试官问这个问题看的不是你背得多熟而是你能不能按层次把过程讲清楚遇到追问能不能深入。我建议按照时间线来答思路清晰也不容易漏输入URL后浏览器先解析URL判断协议、域名、端口然后处理缓存如果命中强缓存直接走缓存流程不发请求DNS解析把域名解析成IP地址依次查找浏览器缓存、系统缓存、路由器缓存、DNS服务器建立TCP连接三次握手发送HTTP请求浏览器向服务器发起请求行、请求头、请求体服务器处理请求并返回返回响应状态码、响应头和响应体浏览器解析渲染页面如果请求的是HTML就走渲染流程如果请求的是接口数据则交给JavaScript处理这个流程里每一个环节都能单独拆出来深挖比如DNS解析的细节、TCP三次握手为什么是三次、HTTP和HTTPS的区别、浏览器缓存策略等。所以这道题与其说是一道题不如说是一个“母题”能把这一整条链路相关的八股文全部串起来。3.2 浏览器渲染流程与回流重绘页面渲染的流程也是面试官喜欢追着问的。核心过程是解析HTML生成DOM树解析CSS生成CSSOM树两者合并生成渲染树Render Tree然后计算布局Layout和绘制Paint。这里有个很容易被问到的点**什么是渲染树**渲染树并不是DOM树和CSSOM树简单相加它只包含可见元素。比如设置了display: none的元素不会出现在渲染树里但visibility: hidden的元素虽然不可见仍然会占位所以会出现在渲染树里。这个区别面试中经常考到。然后是回流Reflow和重绘Repaint。回流是指元素的尺寸、位置、结构发生变化时需要重新计算布局重绘是指元素样式改变但布局没变的重新绘制。回流必然导致重绘但重绘不一定会回流。一道高频题是“怎么减少回流和重绘”。常规答案包括使用transform和opacity实现动画因为这两个属性触发的是合成Composite阶段不走回流重绘批量修改DOM比如用DocumentFragment或者先把元素设置为display: none修改完再显示缓存布局信息避免在循环里反复读取offsetTop、offsetHeight等属性尽量通过修改className来一次性变更样式而不是逐条修改style但要记住这些常规答案之外面试官更想听的是你真正遇到过性能问题后的排查思路。所以回答的时候建议结合一个实际案例比如你之前优化过一个列表渲染卡顿问题怎么定位到是频繁回流怎么通过批量处理解决的。项目里没做过就去找个小demo重新体会一下这种实操感是背八股文背不出来的。3.3 浏览器缓存强缓存与协商缓存浏览器缓存几乎是每场面试必问的因为它是前端性能优化里最直接、见效最快的一环。先分清两类缓存强缓存命中后直接使用本地缓存不发请求。通过Expires绝对时间和Cache-Control相对时间控制其中Cache-Control优先级更高协商缓存本地有缓存但不确定是否过期需要向服务器发请求确认。通过Last-Modified / If-Modified-Since和ETag / If-None-Match控制服务器返回304就用本地缓存返回200就更新缓存面试时经常被追问“强缓存和协商缓存怎么选”。我的理解是强缓存适合长时间不变的静态资源比如带hash指纹的JS、CSS文件协商缓存适合需要时不时校验、但又不想每次全量下载的资源比如接口返回的配置信息。补充一个细节请求头里的Cache-Control: no-cache并不等于不缓存它意思是“每次使用前都要去服务器验证一下”实际上走的是协商缓存逻辑。而no-store才是真正的不缓存。这两个概念极容易混淆面试里说出来会很加分。3.4 跨域问题从同源策略到解决方案跨域是前端面试里绕不开的坎。首先要理解为什么会有跨域限制——浏览器的同源策略即协议、域名、端口三者必须一致否则浏览器会拦截响应。这是浏览器出于安全考虑的机制不是服务器主动屏蔽你。常见的解决思路有这么几种JSONP利用script标签不受同源限制的特性只能支持GET请求而且现在用得比较少了CORS后端在响应头里设置Access-Control-Allow-Origin等字段这是目前最标准的方案代理转发开发环境用webpack的devServer.proxy生产环境用Nginx反向代理转发请求postMessage主要用于iframe和window.open打开的窗口之间的跨窗口通信面试官如果问“CORS的简单请求和预检请求有什么区别”很多人会卡住。简单请求是指方法为GET、POST、HEAD且请求头只包含安全字段的请求不需要预检预检请求是浏览器先用OPTIONS方法去问服务器“允许我发这个请求吗”拿到允许的响应后再发真实请求。触发预检的条件是使用了PUT、DELETE等方法或者自定义了请求头或者Content-Type不是三种简单类型之一。这块的难点是容易站在前端视角看问题忽略了跨域其实是浏览器行为。把机制讲清楚再列出你在实际项目中用过的方案这题就稳了。4. HTML与CSS面试里的“送分题”与“暗坑”4.1 语义化标签和盒模型HTML和CSS在面试里占比不高但偶尔会出现一些看似简单、实际暗藏杀机的题目。语义化标签是个高频点面试官会问“为什么要用语义化标签”标准答法是利于SEO、增强可访问性、方便代码维护。但更高级的答法是举一个具体的例子比如一个页面里h1只应该出现一次由div堆出来的“伪标题”在屏幕阅读器里完全读不出来对盲人用户非常不友好。这种细节会让面试官觉得你是真的在意可访问性而不是背概念。盒模型有两个版本标准盒模型box-sizing: content-box和怪异盒模型box-sizing: border-box。区别在于width的计算范围标准盒模型的width只包含内容区不包含padding和border怪异盒模型的width包含content padding border。实际开发中我基本所有项目都会设置全局的box-sizing: border-box因为这样在给元素设置宽度时心里有数不会被多出来的padding撑破布局。这个细节你可以在面试中提到会显得你有实际工程经验。4.2 垂直水平居中的N种姿势垂直水平居中面试中经常让现场写样式。别以为简单越基础的问题越能看出平时的积累。至少有这些方案使用flex布局display: flex; justify-content: center; align-items: center;使用grid布局display: grid; place-items: center;绝对定位 transformposition: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);绝对定位 margin: auto需要给元素设置固定宽高然后top/left/right/bottom全为0margin: auto面试时建议先把flex方案写出来因为这几乎是现在兼容性最好、最常用的方案。然后补充说明grid布局更简洁但需要注意兼容性。最后再说说绝对定位方案的适用场景比如弹窗层。这样答既能展现广度又能体现你对不同方案的取舍思考。4.3 Flex布局高频考点Flex布局在CSS面试里出镜率极高因为现在几乎没有不用Flex的页面了。核心考点是justify-content、align-items、flex-direction这几个属性的作用但更深一层会问flex属性的简写含义。flex: 1其实是flex-grow: 1; flex-shrink: 1; flex-basis: 0%的简写意思是这个元素在主轴方向上按比例占据剩余空间。很多人只知道flex: 1能让元素自适应撑开但被追问拆开后反而说不清楚。面试官还喜欢让现场实现一个经典布局左侧固定宽度200px右侧自适应撑满剩余宽度。用Flex的话.container { display: flex; } .left { width: 200px; flex-shrink: 0; } .right { flex: 1; }关键点是给左侧加上flex-shrink: 0防止容器宽度不足时左侧被压缩。这个细节是很多新手容易忽略的在面试时主动提出来会让面试官觉得你写代码时考虑过边界情况。5. 初相遇避坑指南常见错误与心态5.1 背诵式回答为什么撑不过三连追问我最开始复习八股文时犯过一个典型错误每个问题都能背出标准答案但面试官只要多问一句“为什么”就露馅。比如他能背出“display: none和visibility: hidden的区别”但问“哪个会触发回流”时就愣住了。后来我总结出一个规律面试官的三连问其实是在顺着“是什么-为什么-怎么办”层层往下挖。你会背的是“是什么”这一层但真正的分数落在“为什么”和“怎么办”上。拿闭包举例是什么函数记住并访问外部作用域变量的能力为什么因为函数定义时会把外部作用域链保存下来怎么办利用它做数据私有化、记忆化但注意别造成内存泄漏每一层都要准备一两个例子。这是八股文复习里性价比最高的方法问题总量没变但每个问题的深度加了面试时能扛住的追问就会多出好几轮。5.2 手写题翻车现场这几道题考前必须练初相遇这一集至少有这几道手写题建议考前闭着眼睛都能写出来。new的手写实现前面给过代码call / apply / bind的手写实现深拷贝要考虑数组、对象、循环引用防抖和节流数组去重和拍平拿call的简单实现来说核心思路是把你想要绑定的对象临时变成函数的一个属性来调用Function.prototype.myCall function (context, ...args) { context context ?? window; // 兼容 null/undefined const key Symbol(fn); // 用 Symbol 避免属性名冲突 context[key] this; const result context[key](...args); delete context[key]; return result; };这里用Symbol作为临时属性名是个加分项一般面试者会直接用字符串面试官追问“有没有想过属性名冲突怎么办”你答“用Symbol”就体现出了对细节的思考。手写题不只要写对还要解释每一步的意图。防抖和节流也是高频我建议把这两个写在一张卡片上考前最后一天专门过一遍。防抖是“在你停止触发后delay毫秒执行”节流是“每隔delay毫秒执行一次”。很多公司会直接让你写实现写不出来前面答得再好也会大打折扣。5.3 外包、校招、社招的八股文侧重点不同热词里提到“吉利外包前端面试”说明有些朋友在准备外包岗位的面试。不同岗位的考察侧重点确实不一样说点我观察到的规律。外包岗位通常更看重能不能马上上手干活所以面试的问题会更贴近业务使用场景比如“Vue里如何父子组件通信”“接口请求封装怎么做的”纯原理类的问题相对少一些但HTML、CSS、JavaScript基础仍然会考。校招则更看重潜力基础知识占比很高数据结构和算法也经常出现因为应届生没有太多项目经验可聊八股文就成了主要的筛选工具。社招正好相反项目经验占了很大比重八股文更多是“热身题”用来在项目深挖之间穿插。不管哪种岗位前端基石部分都不能有硬伤。所以这个系列的内容对所有类型的面试都有用只是你需要根据自己的目标岗位调整复习重心。5.4 面试画风突变现场如何把八股文聊成技术交流最后一个常见问题是准备了那么多八股文结果面试官问的不是你准备的那些或者问法和标准答案不太一样一下就慌了。这个情况太常见了因为面试官本身就是在不同的题目之间灵活切换的。我的经验是遇到没准备过的问题不要急着说“不会”先尝试从自己熟悉的知识点往那个问题上靠。比如问“浏览器怎么处理一个很大的JSON数据会不会卡顿”你可以从事件循环、渲染机制、Web Worker这些你熟悉的点切入把自己知道的部分讲出来边讲边理思路。就算最终没答到点子上面试官也会看到你在思考而不是直接放弃。把八股文当成技术交流的素材而不是考试的题库这个心态转换很重要。面试官也是人他更愿意跟一个能聊得来的人共事而不是一个背题机器。6. 初相遇自查清单这一集的内容到这里就差不多了。按连续剧的惯例最后留一份自查清单你来对照检查自己有没有真正吃透这一集的内容能画清楚原型链的指向关系能手写new能讲清楚闭包的原理、作用和一个实际应用场景能准确判断一段代码里的this指向并说出依据能排对微任务和宏任务混在一起的输出顺序能按时间线完整讲出从输入URL到页面渲染的过程能区分强缓存和协商缓存并说明各自的适用场景能说出回流和重绘的区别并给出减少回流的方法能手写call、防抖、节流并解释每一步的意图每一项里如果只是“看过”但讲不出来建议回到对应的章节再啃一遍。学八股文没有捷径但它也远没到靠死记硬背的程度。真正理解原理之后你会发现自己写代码时也会更顺手面试只是顺带的结果。下一集我们聊聊JavaScript更深的那些面试题——作用域链、立即执行函数、变量提升这些“初相遇之后”的故事。追剧不急先把这一集的内容消化透我们下集见。
返回列表