千家信息网

浏览器的面试题有哪些

发表于:2024-12-04 作者:千家信息网编辑
千家信息网最后更新 2024年12月04日,本文小编为大家详细介绍"浏览器的面试题有哪些",内容详细,步骤清晰,细节处理妥当,希望这篇"浏览器的面试题有哪些"文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.常见的浏览
千家信息网最后更新 2024年12月04日浏览器的面试题有哪些

本文小编为大家详细介绍"浏览器的面试题有哪些",内容详细,步骤清晰,细节处理妥当,希望这篇"浏览器的面试题有哪些"文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

1.常见的浏览器内核有哪些?

浏览器的内核可以分成两部分:

渲染引擎JS引擎注意:我们常说的浏览器内核就是指渲染引擎

由于JS引擎越来越独立,内核就指的只是渲染引擎了,渲染引擎主要用来请求网络页面资源解析排版后呈现给用户

浏览器/RunTime内核(渲染引擎)JavaScript 引擎
ChromeBlink(28~) Webkit(Chrome 27)V8
FireFoxGeckoSpiderMonkey
SafariWebkitJavaScriptCore
EdgeEdgeHTMLChakra(For JavaScript)
IETridentChakra(For JScript)
OperaPresto->blinkLinear A(4.0-6.1)/ Linear B(7.0-9.2)/ Futhark(9.5-10.2)/ Carakan(10.5-)
Node.js-V8

2.浏览器的主要组成部分有哪些?

  • 用户界面:包括地址栏,前进/后退/刷新/书签等按钮

  • 浏览器引擎:在用户界面和呈现引擎之间传送指令

  • 渲染引擎:用来绘制请求的内容

  • 网络:用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作

  • JavaScript解释器:用来解析执行JavaScript代码

  • 用户界面后端:用于绘制基本的窗口小部件,比如组合框和窗口,底层使用操作系统的用户接口

  • 数据存储:属于持久层,浏览器在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术

注意:与大多数浏览器不同的是,谷歌(Chrome)浏览器的每个标签页都分别对应一个呈现引擎实例。每个标签页都是一个独立的进程

3.说一说从输入URL到页面呈现发生了什么?

这个题可以说是面试最常见也是一道可以无限难的题了,一般面试官出这道题就是为了考察你的前端知识深度。

1、浏览器接受URL开启网络请求线程(涉及到:浏览器机制,线程与进程等)

2、开启网络线程到发出一个完整的http请求(涉及到:DNS查询,TCP/IP请求,5层网络协议等)

3、从服务器接收到请求到对应后台接受到请求(涉及到:负载均衡,安全拦截,后台内部处理等)

4、后台与前台的http交互(涉及到:http头,响应码,报文结构,cookie等)

5、缓存问题(涉及到:http强缓存与协商缓存,缓存头,etag,expired,cache-control等)

6、浏览器接受到http数据包后的解析流程(涉及到html词法分析,解析成DOM树,解析CSS生成CSSOM树,合并生成render渲染树。然后layout布局,painting渲染,复合图层合成,GPU绘制,外链处理等)

7、css可视化模型(涉及到:元素渲染规则,如:包含块,控制框,BFC,IFC等)

8、JS引擎解析过程(涉及到:JS解析阶段,预处理阶段,执行阶段生成执行上下文,VO(全局对象),作用域链,回收机制等)

你会发现一个简单的输入URL到页面呈现,之间会发生这么多过程,是不是瞬间觉得崩溃了?(别急,这一章我们不讲这么深,先教你如何回答这个问题,后面这一节单独出文章讲)

  • 浏览器通过DNS服务器得到域名的IP地址,向这个IP地址请求得到HTML文本

  • 浏览器渲染进程解析HTML文本,构建DOM树

  • 解析HTML的同时,如果遇到内联样式或者样式文件,则下载并构建样式规则,如果遇到JavaScript脚本,则会下载执行脚本

  • DOM树和CSSOM构建完成之后,渲染进程将两者合并成渲染树(render tree)

  • 渲染进程开始对渲染树进行布局,生成布局树(layout tree)

  • 渲染树对布局树进行绘制,生成绘制记录

4.浏览器是如何解析代码的?

解析HTML

HTML是逐行解析的,浏览器的渲染引擎会将HTML文档解析并转换成DOM节点。

  • 将HTML解析成许多Tokens

  • 将Tokens解析成object

  • 将object组合成一个DOM树

解析CSS

浏览器会从右往左解析CSS选择器

我们知道DOM树与CSSOM树合并成render树,实际上是将CSSOM附着到DOM树上,因此需要根据选择器提供的信息对DOM树进行遍历。

我们看一个例子:

从右至左的匹配:

  • 先找到所有的最右节点 span,对于每一个 span,向上寻找节点 div.title

  • 由 h4再向上寻找 div.nav 的节点

  • 最后找到根元素 html 则结束这个分支的遍历。

解析JS

在浏览器中有一个js解析器的工具,专门用来解析我们的js代码。

当浏览器遇到js代码时,立马召唤"js解析器"出来工作。

解析器会找到js当中的所有变量、函数、参数等等,并且把变量赋值为未定义(undefined)。

把函数取出来成为一个函数块,然后存放到仓库当中。这件事情做完了之后才开始逐行解析代码(由上向下,由左向右),然后再去和仓库进行匹配。

5.DOMContentLoaded与load的区别?

  • DOMContentLoaded:仅当DOM解析完成后触发,不包括样式表,图片等资源。

  • Load:当页面上所有的DOM,样式表,脚本,图片等资源加载完毕事触发。

6.浏览器重绘域重排的区别?

  • 重排: 部分渲染树或整个渲染树需要重新分析且节点尺寸需要重新计算,表现为重新生成布局,重新排列元素

  • 重绘: 由于节点的几何属性发生改变或样式改变,例如元素背景元素,表现为某些元素的外观被改变

重绘不一定导致重排,但重排一定绘导致重绘

如何触发重绘和重排?

任何改变用来构建渲染树的信息都会导致一次重排或重绘:

  • 添加、删除、更新DOM节点

  • 通过display: none隐藏一个DOM节点-触发重排和重绘

  • 通过visibility: hidden隐藏一个DOM节点-只触发重绘,因为没有几何变化

  • 移动或者给页面中的DOM节点添加动画

  • 添加一个样式表,调整样式属性

  • 用户行为,例如调整窗口大小,改变字号,或者滚动。

如何避免重绘或重排?

  • 集中改变样式:比如使用class的方式来集中改变样式

  • 使用document.createDocumentFragment():我们可以通过createDocumentFragment创建一个游离于DOM树之外的节点,然后在此节点上批量操作,最后插入DOM树中,因此只触发一次重排

  • 提升为合成层

    将元素提升为合成层有以下优点:

    • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快

    • 当需要repaint 时,只需要 repaint 本身,不会影响到其他的层

    • 对于 transformopacity 效果,不会触发 layoutpaint

      提升合成层的最好方式是使用 CSS 的 will-change 属性

7.为什么JS是单线程的?

这主要与JS的用途有关,JS作为浏览器的脚本语言,最初主要是实现用户与浏览器的交互,以及操作DOM。这就决定了它只能是单线程,否则会带来许多复杂的同步问题。

举个例子: 如果JS是多线程的,其中一个线程要修改一个DOM元素,另外一个线程想要删除这个DOM元素,这时候浏览器就不知道该听谁的。所以为了避免复杂性,从一诞生,JavaScript就被设计成单线程。

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质

8.CSS加载会阻塞DOM吗?

先上结论

  • CSS不会阻塞DOM的解析,但会阻塞DOM的渲染

  • CSS会阻塞JS执行,但不会阻塞JS文件的下载

CSSOM的作用

  • 第一个是提供给JavaScript操作样式表的能力

  • 第二个是为布局树的合成提供基础的样式信息

  • 这个CSSOM体现在DOM中就是document.styleSheets

由之前讲到的浏览器渲染流程我们可以看出:

  • DOM和CSSOM通常是并行构建的,所以CSS加载不会阻塞DOM的解析

  • render树是依赖DOM树和CSSOM树的,所以它必须等到两者都加载完毕才能开始构建渲染,所以CSS加载会阻塞DOM的渲染

  • 由于JavaScript是可以操作DOM与CSS的,如果在修改这些元素属性同时渲染界面(即JavaScript线程与UI线程同时进行),那么渲染线程前后获得的元素可能就不一致了。所以为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JavaScript线程为互斥的关系

JS需要等待CSS的下载,这是为什么呢?(CSS阻塞DOM执行)

如果JS脚本的内容是获取元素的样式,那它就必然依赖CSS。因为浏览器无法感知JS内部到底想干什么,为避免样式获取,就只好等前面所有的样式下载完毕再执行JS。但JS文件与CSS文件下载是并行的,CSS文件会在后面的JS文件执行前先加载执行完毕,所以CSS会阻塞后面JS的执行

避免白屏,提高CSS的加载速度

  • 使用CDN(CDN会根据你的网络状况,挑选最近的一个具有缓存内容的节点为你提供资源,因此可以减少加载时间)

  • 对CSS进行压缩

  • 合理使用缓存

  • 减少http请求数,合并CSS文件

9.JS会阻塞页面吗?

先上结论

JS会阻塞DOM的解析,因此也就会阻塞页面的加载

这也是为什么我们常说要把JS文件放在最下面的原因

由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。

因此为了防止渲染出现不可预期的结果,浏览器设置 **「GUI 渲染线程与 JavaScript 引擎为互斥」**的关系。

当 JavaScript 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。

当浏览器在执行 JavaScript 程序的时候,GUI 渲染线程会被保存在一个队列中,直到 JS 程序执行完成,才会接着执行。

因此如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

10.defer和async的区别?

  • 两者都是异步去加载外部JS文件,不会阻塞DOM解析

  • Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行,标记为async的脚本并不保证按照指定他们的先后顺序执行,该属性对于内联脚本无作用 (即没有**「src」**属性的脚本)。

  • defer是在JS加载完成后,整个文档解析完成后,触发 DOMContentLoaded 事件前执行,如果缺少 src 属性(即内嵌脚本),该属性不应被使用,因为这种情况下它不起作用

11.浏览器的垃圾回收机制

垃圾回收是一种自动的内存管理机制。当计算机上的动态内存不再需要时,就应该予以释放。

需要注意的是,自动的意思是浏览器可以自动帮助我们回收内存垃圾,但并不代表我们不用关心内存管理,如果操作不当,JavaScript中仍然会出现内存溢出的情况,造成系统崩溃。

由于字符串,数组,对象等都没有固定大小,因此需要当它们大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串,数组或对象时,解释器都必须分配内存来存储那个实体。

JavaScript解释器可以检测到何时程序不在使用一个对象了,当它确定这个对象是无用的时候,他就知道不再需要这个对象了,就可以把它占用的内存释放掉了。

浏览器通常采用的垃圾回收有两种方法:标记清除引用计数

标记清除

这是JavaScript中最常用的垃圾回收方式

从2012年起,所有现代浏览器都使用了标记清除的垃圾回收方法,除了低版本IE还是采用的引用计数法。

那么什么叫标记清除呢?

JavaScript中有一个全局对象,定期的,垃圾回收器将从这个全局对象开始,找出所有从这个全局对象开始引用的对象,再找这些对象引用的对象...对这些活跃的对象标记,这是标记阶段。清楚阶段就是清楚那些没有被标记的对象。

标记清除有一个问题,就是在清除之后,内存空间是不连续的,即出现了内存碎片。如果后面需要一个比较大的连续的内存空间,那将不能满足要求。而标记整理 方法可以有效德地解决这个问题。

在标记的过程中,引入了概念:三色标记法,三色为:

  • 白:未被标记的对象,即不可达对象(没有扫描到的对象),可回收

  • 灰:已被标记的对象(可达对象),但是对象还没有被扫描完,不可回收

  • 黑:已被扫描完(可达对象),不可回收

标记整理:

标记阶段与标记清除法没什么区别,只是标记结束后,标记整理法会将存活的对象向内存的一边移动,最后清理掉边界内存。

引用计数

引用计数的含义是跟踪记录每个值被引用的次数。当一个变量A被赋值时,这个值的引用次数就是1,当变量A重新赋值后,则之前那个值的引用次数就减1。当引用次数变成0时,则说明没有办法再访问这个值了,所以就可以清除这个值占用的内存了。

大多数浏览器已经放弃了这种回收方式

内存泄漏

为避免内存泄漏,一旦数据不再使用,最好通过将其值设为null来释放其引用,这个方法叫做接触引用

哪些情况会造成内存泄漏?如何避免?

以 Vue 为例,通常有这些情况:

  • 监听在 window/body 等事件没有解绑

  • 绑在 EventBus 的事件没有解绑

  • Vuex$storewatch 了之后没有 unwatch

  • 使用第三方库创建,没有调用正确的销毁函数

解决办法:beforeDestroy 中及时销毁

  • 绑定了 DOM/BOM 对象中的事件 addEventListenerremoveEventListener

  • 观察者模式 $on$off处理。

  • 如果组件中使用了定时器,应销毁处理。

  • 如果在 mounted/created 钩子中使用了第三方库初始化,对应的销毁。

  • 使用弱引用 weakMapweakSet

浏览器中不同类型变量的内存都是何时释放的?

  • 引用类型

    • 在没有引用之后,通过 V8 自动回收。

  • 基本类型

    • 如果处于闭包的情况下,要等闭包没有引用才会被 V8 回收。

    • 非闭包的情况下,等待 V8 的新生代切换的时候回收。

12.说一说浏览器的缓存机制?

认识浏览器缓存

当浏览器请求一个网站时,会加载各种资源,对于一些不经常变动的资源,浏览器会将他们保存在本地内存中,下次访问时直接加载这些资源,提高访问速度。

如何知道资源是请求的服务器还是读取的缓存呢?

看上面这张图,有些资源的size值是大小,有些是from disk cache,有些是from memory cache,显示大小的是请求的服务器资源,而显示后面两种的则是读取的缓存。

  • disk cache: 就是将资源存储在磁盘中,等待下次访问时不需重新下载,直接从磁盘中读取,它的直接操作对象为CurlCacheManager。(效率比内存缓存慢,但存储容量大,存储时间长)

  • memory cache: 就是将资源缓存到内存中,等待下次访问时不需重新下载,直接从内存中读取。(从效率上看它是最快的,从存活时间来看,它是最短的。)

-memory cachedisk cache
相同点只能存储一些派生类资源文件只能存储一些派生类资源文件
不同点退出进程时数据会被清除退出进程时数据不会被清除
存储资源一般脚本、字体、图片会存在内存当中一般非脚本会存在内存当中,如css等

浏览器缓存分类

  • 强缓存

  • 协商缓存

浏览器在向服务器请求资源时,首先判断是否命中强缓存,没命中再判断是否命中协商缓存

强缓存

浏览器在加载资源时,会先根据本地缓存资源的header中判断是否命中强缓存,如果命中则直接使用缓存中的资源,不会再向服务器发送请求。 (这里的header中的信息指的是 expirescache-control

  • Expires

该字段是 http1.0 时的规范,它的值为一个绝对时间的 GMT 格式的时间字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT。这个时间代表着这个资源的失效时间,在此时间之前,即命中缓存。这种方式有一个明显的缺点,由于失效时间是一个绝对时间,所以当服务器与客户端时间偏差较大时,就会导致缓存混乱。所以这种方式很快在后来的HTTP1.1版本中被抛弃了。

  • Cache-Control

Cache-Control 是 http1.1 时出现的 header 信息,主要是利用该字段的 max-age 值来进行判断,它是一个相对时间,例如 Cache-Control:max-age=3600,代表着资源的有效期是 3600 秒。cache-control 除了该字段外,还有下面几个比较常用的设置值:

no-cache:需要进行协商缓存,发送请求到服务器确认是否使用缓存。

no-store:禁止使用缓存,每一次都要重新请求数据。

public:可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。

private:只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。

Cache-Control 与 Expires 可以在服务端配置同时启用,同时启用的时候 Cache-Control 优先级高。

协商缓存

当强缓存没命中时,浏览器会发送一个请求到服务器,服务器根据 header 中的信息来判断是否命中协商缓存。如果命中,则返回304 ,告诉浏览器资源未更新,可以使用本地缓存。 (这里的header信息指的是Last-Modify/If-Modify-SinceETag/If-None-Match

  • Last-Modify/If-Modify-Since

浏览器第一次请求一个资源的时候,服务器返回的 header 中会加上 Last-Modify,Last-modify 是一个时间标识该资源的最后修改时间。

当浏览器再次请求该资源时,request 的请求头中会包含 If-Modify-Since,该值为缓存之前返回的 Last-Modify。服务器收到 If-Modify-Since 后,根据资源的最后修改时间判断是否命中缓存。

如果命中缓存,则返回 304,并且不会返回资源内容,并且不会返回 Last-Modify。

缺点:

短时间内资源发生了改变,Last-Modified 并不会发生变化。

周期性变化。如果这个资源在一个周期内修改回原来的样子了,我们认为是可以使用缓存的,但是 Last-Modified 可不这样认为,因此便有了 ETag。

  • ETag/If-None-Match

与 Last-Modify/If-Modify-Since 不同的是,Etag/If-None-Match 返回的是一个校验码。ETag 可以保证每一个资源是唯一的,资源变化都会导致 ETag 变化。服务器根据浏览器上送的 If-None-Match 值来判断是否命中缓存。

与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于 ETag 重新生成过,response header 中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。

Last-Modified 与 ETag 是可以一起使用的,服务器会优先验证 ETag,一致的情况下,才会继续比对 Last-Modified,最后才决定是否返回 304。

总结

当浏览器访问一个已经访问过的资源是,它的步骤是:

1.先看是否命中强缓存,命中?的话直接使用缓存

2.没命中强缓存,则会发送请求到服务器看是否命中?协商缓存

3.如果命中了协商缓存,服务器会返回304告诉浏览器可以使用本地缓存

4.没命中协商缓存,则服务器会返回新的资源给浏览器

13.什么是浏览器的同源策略,以及跨域?

同源策略

同源策略是浏览器的一种自我保护行为。所谓的同源指的是:协议,域名,端口均要相同

浏览器中大部分内容都是受同源策略限制的,但是以下三个标签不受限制:

跨域

跨域指的是浏览器不能执行其它域名下的脚本。它是由浏览器的同源策略限制的。

你可能会想跨域请求到底有没有发送到服务器?

事实上,跨域请求时能够发送到服务器的,并且服务器也能过接受的请求并正常返回结果,只是结果被浏览器拦截了。

跨域解决方案(列出几个常用的)
  • JSONP

它主要是利用script标签不受浏览器同源策略的限制,可以拿到从其他源传输过来的数据,需要服务端支持。

优缺点:

兼容性比较好,可用于解决主流浏览器的跨域数据访问的问题。缺点就是仅支持get请求,具有局限性,不安全,可能会受到XSS攻击。

思路:

  • 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)。

0