Skip to content

JavaScript 面试基础知识(中篇)

1. 什么是进程?什么又是线程

特性进程线程
定义程序的独立运行实例进程内的最小执行单位
资源分配拥有独立的资源和内存空间共享所在进程的资源和内存
开销创建、销毁、切换开销较大开销较小
执行效率并行执行效率相对低在多核 CPU 中更易实现并发
稳定性进程崩溃不影响其他进程线程崩溃会影响所在进程
通信方式需要进程间通信(IPC)可以直接访问进程中的共享数据
使用场景高隔离需求的任务,独立运行的程序多任务并发,如并行处理和任务分配

2. 并发与并行的区别

并发并行都是处理多个任务的概念,但它们有一些关键的不同:

并发(Concurrency)

并发是指在同一时间段内处理多个任务。在并发的情况下,多个任务可以交替执行,但不一定同时执行。并发主要依靠时间片轮转等机制让任务看起来像是“同时”进行,但在某个时刻只有一个任务在执行。

  • 实现方式:通过任务切换,在不同任务之间快速切换,使得用户感觉到多个任务同时进行。
  • 应用场景:适用于单核或多核系统,尤其是 I/O 密集型任务,比如网络请求、文件读写等。
  • 例子:假设一个单核处理器在短时间内快速交替执行任务 A 和任务 B,看起来像是它们在同时执行,但实际上是快速切换的结果。

并行(Parallelism)

并行是指在相同时间点上执行多个任务。并行需要多核 CPU 或多台处理器同时工作,真正地做到同时运行多个任务。

  • 实现方式:在多核 CPU 或多台处理器上让不同任务在不同核上真正地同时执行。
  • 应用场景:多核系统的计算密集型任务,比如图像处理、科学计算等。
  • 例子:在四核 CPU 上同时运行四个独立的计算任务,每个任务在不同的核心上运行,真正地在同一时刻完成多个任务。

并发与并行的区别

特性并发并行
定义同一时间段内处理多个任务同一时间点上执行多个任务
执行方式任务交替执行,快速切换任务在不同的处理器或核心上同时执行
系统需求适用于单核或多核系统需要多核系统或多处理器
适用场景I/O 密集型任务计算密集型任务
示例单核 CPU 交替执行多个任务多核 CPU 上同时执行多个任务

总结

  • 并发:任务交替执行,在同一时间段内处理多个任务。
  • 并行:任务真正同时执行,在同一时刻完成多个任务。

并发可以在单核系统中实现,而并行需要多核或多处理器系统来实现。

3.什么是重绘(Repaint)和回流(Reflow)

重绘回流是浏览器渲染页面的两个关键过程,主要影响页面性能和用户体验。理解它们的区别可以帮助优化页面渲染,提高性能。

1. 什么是重绘(Repaint)

重绘是指当元素的外观发生变化,但不影响布局时,浏览器重新渲染元素的过程。例如更改元素的颜色、背景图片或可见性等属性。

  • 触发条件:只更改元素的外观(如颜色、背景等),不涉及尺寸、位置或结构变化。
  • 性能影响:重绘的开销相对较小,因为浏览器只需更新元素的外观,而不需要重新计算布局。
  • 示例
    javascript
    element.style.color = "red"; // 更改颜色,触发重绘
    element.style.visibility = "hidden"; // 更改可见性,触发重绘

2. 什么是回流(Reflow)

回流(也称重排)是指当页面中元素的尺寸、位置、布局或结构发生变化时,浏览器需要重新计算元素的布局并重新渲染的过程。回流是一个更耗性能的过程,因为浏览器需要重新计算所有相关元素的位置和布局。

  • 触发条件:任何改变元素布局或结构的操作(如添加、删除元素,改变元素尺寸、边距、边框、位置等)都会触发回流。
  • 性能影响:回流的性能开销较大,因为不仅需要重新计算该元素,还可能会影响其他元素,甚至导致整个页面的重新布局。
  • 示例
    javascript
    element.style.width = "200px"; // 更改宽度,触发回流
    element.style.marginTop = "20px"; // 更改外边距,触发回流

如何减少重绘和回流

为了提升页面性能,减少不必要的重绘和回流,可以采取以下优化策略:

  1. 合并操作:合并多次对元素样式的更改,尽量减少单独更改样式的次数。例如,可以使用 class 一次性更改多个属性。
  2. 减少 DOM 操作:频繁的 DOM 操作会导致回流,建议减少直接操作 DOM。可以使用 DocumentFragment 或虚拟 DOM 来批量更新,降低操作频率。
  3. 避免频繁读取样式:读取样式会触发浏览器重新计算布局,建议将读取和修改样式的操作分开,避免频繁读取。
  4. 使用 transformopacity 属性:这些属性只会触发重绘,不会导致回流,适合做动画效果。
  5. CSS 动画优化:将动画应用于 transformopacity 属性,避免改变尺寸或位置等会导致回流的属性。
  6. 减少复杂选择器:复杂的 CSS 选择器会增加计算时间,使用更简单的选择器可以提升性能。
  7. 批量操作样式:通过 element.style.cssText 一次性应用多项样式,而不是多次单独应用。
  8. 使用 will-change:对于需要动画或频繁变化的属性,可以通过 will-change 属性提前通知浏览器进行优化处理。

重绘和回流的对比

特性重绘(Repaint)回流(Reflow)
定义仅更新元素的外观(如颜色),不影响布局更新元素的布局、尺寸或结构,重新计算布局
触发条件改变颜色、背景、可见性等改变尺寸、位置、结构等
性能影响开销较小,只需更新外观开销较大,重新计算布局,可能影响整个页面
优化建议尽量合并外观更新,减少样式变动频率合并布局更新、减少 DOM 操作、使用 transform/opacity 属性
示例colorbackgroundwidthheightdisplaymargin

4. 浏览器存储都有哪些?

存储方式用途容量限制有效期访问方式特点
Cookie存储少量文本数据,如用户登录状态等每个 Cookie 约 4 KB可设置过期时间,默认会话结束时失效通过 JavaScript 访问,HTTP 请求/响应中也可使用随每个 HTTP 请求发送,安全性较低,容易被篡改
LocalStorage存储长期数据,如用户设置、配置信息等5-10 MB永久存储,除非手动清除通过 JavaScript 访问不随请求发送数据,支持跨页面存取数据
SessionStorage存储会话期间的数据,如一次会话中的临时数据5-10 MB浏览器会话结束时失效通过 JavaScript 访问仅在当前窗口或标签页有效,窗口关闭时数据清除
IndexedDB存储结构化数据,适用于大数据存储和查询没有严格限制,取决于浏览器和设备永久存储,除非手动删除通过 JavaScript API 访问支持事务操作,存储结构化数据,适合大数据存储
Web SQL Database(已弃用)存储结构化数据,基于 SQL 的数据库存储5 MB 或更大永久存储,除非手动删除通过 SQL 查询语句访问已弃用,未来不再支持,建议使用 IndexedDB
Cache API存储资源(文件、图像、JS、CSS 等),主要用于离线缓存容量通常较大可设置过期时间,过期后删除通过 JavaScript API 访问适用于 PWA 离线支持,自动管理缓存空间

明确一点,localStorage 是同步的

是的,硬盘确实是一个 IO 设备,而大部分与硬盘相关的操作系统级 IO 操作确实是异步进行的,以避免阻塞进程。不过,在 Web 浏览器环境中,localStorage  的 API 是设计为同步的,即使底层的硬盘读写操作有着 IO 的特性。 js 代码在访问  localStorage  时,浏览器提供的 API 接口通常会处于 js 执行线程上下文中直接调用。这意味着尽管硬盘是 IO 设备,当一个 js 执行流程访问  localStorage  时,它将同步地等待数据读取或写入完成,该过程中 js 执行线程会阻塞。 这种同步 API 设计意味着开发者在操作  localStorage  时不需要考虑回调函数或者 Promise 等异步处理模式,可以按照同步代码的方式来编写。不过,这也意味着如果涉及较多数据的读写操作时,可能对性能产生负面影响,特别是在主线程上,因为它会阻塞 UI 的更新和其他 js 的执行。

5. 谈谈三种事件模型

事件模型

  1. DOM0 级模型: ,这种模型不会传播,所以没有事件流的概念,但是现在有的浏览器支持以冒泡的方式实现,它可以在网页中直接定义监听函数,也可以通过 js 属性来指定监听函数。这种方式是所有浏览器都兼容的。

  2. IE 事件模型: 在该事件模型中,一次事件共有两个过程,事件处理阶段,和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。

  3. DOM2 级事件模型: 在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。后面两个阶段和 IE 事件模型的两个阶段相同。这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。

事件委托

事件委托指的是把一个元素的事件委托到另外一个元素上。一般来讲,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

Event event

阻止默认事件: event.stopPropagation 阻止事件默认行为: event.preventDefault()

6.自定义事件

目前实现自定义事件的方式共两种

  1. Event()
javascript
const sendEvent = new Event("sendMsg");
document.addEventListener("sendMsg", print);
document.dispatchEvent(sendEvent);
function print() {
  console.log("hello custom event");
}
// result:hello custom event。
  1. CustomEvent()
javascript
const sendEvent = new CustomEvent("sendMsg", {
  detail: {
    name: "good luck",
  },
});
document.addEventListener("sendMsg", print);
document.dispatchEvent(sendEvent);
function print(e) {
  console.log(e.detail.name);
}

区别:

通过上面两个简单的例子我们可以看出,Event 和 CustomEvent 最大的区别在于,CustomEvent 可以传递数据

7. 前端模块化的理解

JavaScript 模块化规范对比

特性CommonJSAMD (Asynchronous Module Definition)CMD (Common Module Definition)ES6 模块
使用场景Node.js 服务端浏览器(前端异步加载)浏览器(前端异步加载)浏览器、Node.js 等多环境
导入语法requirerequirerequireimport
导出语法module.exports / exportsdefinedefineexport
加载方式同步加载(阻塞式)异步加载懒加载,按需加载静态加载,支持动态 import()
依赖处理编写时就确定依赖,加载整个模块依赖提前加载依赖在使用时加载依赖提前加载,可静态分析
是否支持静态分析
适用场景服务端开发,适合同步加载浏览器,适合异步加载浏览器,按需加载现代浏览器和 Node.js 的模块化方案
实现库Node.js 标准模块系统RequireJSSea.js原生支持,无需额外库

ES6 模块与 CommonJS 模块有什么异同?

  • CommonJS 是对模块的浅拷⻉,ES6 Module 是对模块的引⽤,即 ES6 Module 只存只读,不能改变其值,也就是指针指向不能变,类似 const;
  • import 的接⼝是 read-only(只读状态),不能修改其变量值。 即不能修改其变量的指针指向,但可以改变变量内部指针指向,可以对 commonJS 对重新赋值(改变指针指向),但是对 ES6 Module 赋值会编译报错。

ES6 Module 和 CommonJS 模块的共同点:

  • CommonJS 和 ES6 Module 都可以对引⼊的对象进⾏赋值,即对对象内部属性的值进⾏改变。

9. 什么是尾调用,使用尾调用有什么好处?

尾调用指的是函数的最后一步调用另一个函数。使用尾调用的话,因为已经是函数的最后一步,所以这时可以不必再保留当前的执行上下文,从而节省了内存,这就是尾调用优化。但是 ES6 的尾调用优化只在严格模式下开启,正常模式是无效的。

10. JSON 与普通文本、XML 对比

特性JSON普通文本XML
数据格式键值对结构自由文本,无结构约束标签嵌套结构
可读性简洁清晰,易于阅读视内容而定,结构性较差结构复杂,语法较冗长
数据体积较小,适合传输取决于内容通常较大,包含冗余标签
结构化支持支持对象和数组结构不支持支持层次化的嵌套结构
数据类型支持支持字符串、数字、布尔值、数组、对象等通常为字符串仅支持字符串
解析效率较高,直接映射到数据结构无需解析相对较低
可扩展性无注释支持,不支持复杂结构不支持扩展支持注释及自定义标签
跨语言兼容性高,多数编程语言支持解析较低较高,多数编程语言支持解析
安全性安全性较好,但需防止 XSS 等安全问题取决于内容安全性较好,易处理恶意数据
典型应用场景前后端数据交换、配置文件、日志格式简单数据记录、消息日志复杂数据结构的文档、配置文件

总结

  • JSON:轻量、结构化,适合网络传输和数据交换。
  • 普通文本:无结构约束,适合简单记录。
  • XML:灵活性高,适合复杂文档和配置数据。

11. Map 与 Object 的区别

特性MapObject
键的类型键可以是任意类型(对象、函数、基本数据类型等)键只能是字符串或 Symbol 类型
键值对的插入顺序保留插入顺序,迭代时按插入顺序没有顺序,属性顺序可能与插入顺序不同
键值对数量使用 size 属性获取元素数量使用 Object.keys(obj).length 获取元素数量
性能在频繁增删数据时性能更高通常较慢,适合少量数据操作
遍历方式使用 forEach() 或迭代器 map.keys() 等方法使用 for...inObject.keys() 方法
默认属性没有默认属性,所有键值都是用户定义的继承自原型链,可能带有 toString 等属性
序列化支持不支持 JSON.stringify() 序列化支持 JSON.stringify() 序列化
常见应用场景需要键值对存储,键类型灵活,数据量较大时更优适合少量简单数据存储,如 JSON 结构
API 和方法支持提供丰富的 API,如 setgethas无专用 API,需自行实现功能
垃圾回收不支持弱引用不支持弱引用

总结

  • Map 更适合频繁增删操作和需要使用非字符串键的场景。
  • Object 更适合轻量级的数据存储,例如 JSON 数据结构。

12. 为什么函数的 arguments 参数是类数组而不是数组?如何遍历类数组?

arguments 是一个对象,它的属性是从 0 开始依次递增的数字,还有calleelength等属性,与数组相似;但是它却没有数组常见的方法属性,如forEach, reduce等,所以叫它们类数组。

要遍历类数组,有三个方法:

javascript
function foo() {
  Array.prototype.forEach.call(arguments, (a) => console.log(a));
  // Array.prototype.slice(arguments)
}

function foo() {
  const arrArgs = Array.from(arguments);
  arrArgs.forEach((a) => console.log(a));
}

function foo() {
  const arrArgs = [...arguments];
  arrArgs.forEach((a) => console.log(a));
}

13. 什么是 DOM 和 BOM?

  • DOM 指的是文档对象模型,它指的是把文档当做一个对象,这个对象主要定义了处理网页内容的方法和接口。
  • BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。BOM 的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 location 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对象的子对象。

14. AJAX、Axios 和 Fetch 的区别

特性AJAXAxiosFetch
定义AJAX(Asynchronous JavaScript and XML)是一种用于网页与服务器异步通信的技术。Axios 是一个基于 Promise 的 HTTP 客户端库,用于浏览器和 Node.js 中发送 HTTP 请求。Fetch 是浏览器内置的 API,用于发送网络请求,基于 Promise。
是否基于 Promise否,传统的 AJAX 使用回调函数处理异步请求。是,基于 Promise,支持异步操作。是,基于 Promise,简化了处理异步请求的方式。
浏览器支持所有现代浏览器均支持,但老版本 IE 需要使用 XMLHttpRequest所有现代浏览器支持。所有现代浏览器支持,但 IE 不支持。
请求方式通过 XMLHttpRequest 或者 jQuery 的 $.ajax() 实现。基于 XMLHttpRequest,对其进行了封装,提供更简单的 API。使用 window.fetch(),语法简单且直观。
处理响应数据的方式需要手动解析响应数据,通常通过回调函数处理。自动将响应数据转换为 JSON,支持 .then().catch() 处理。自动将响应数据转换为流,使用 .then().catch() 处理。
请求和响应拦截器需要手动实现,通常使用 XMLHttpRequest 或 jQuery 插件来拦截请求和响应。提供请求和响应拦截器,方便进行统一处理(如 token、错误处理)。没有内建的拦截器,需要额外的实现。
错误处理通过回调函数处理错误,需要手动检查状态码。通过 .catch() 处理 Promise 错误,支持更简洁的错误处理。通过 .catch() 处理 Promise 错误,需要手动检查响应状态码。
跨域请求需要使用 CORS 或 JSONP 等技术来处理跨域问题。支持跨域请求(通过设置 withCredentials)。支持跨域请求(通过 CORS 配置)。
请求取消可以通过 XMLHttpRequest.abort() 手动取消请求。提供 cancelToken 机制,支持取消请求。通过 AbortController 实现取消请求。
默认支持 JSON需要手动设置响应数据类型。默认支持 JSON 格式的数据。需要通过 .json() 方法手动解析响应数据。
简易性需要更多的配置和代码。相对简单,API 使用直观。API 简单,易于使用。

总结

  • AJAX 是早期的技术,通过 XMLHttpRequest 实现异步请求,但使用起来相对复杂。
  • Axios 是一个流行的基于 Promise 的库,简化了 HTTP 请求的过程,提供了更强大的功能,如请求拦截器和取消请求等。
  • Fetch 是现代浏览器内置的 API,简洁、基于 Promise,但仍需要手动处理一些细节(如解析 JSON 和检查响应状态码)。

fetch 的优点:

  • 语法简洁,更加语义化
  • 基于标准 Promise 实现,支持 async/await
  • 更加底层,提供的 API 丰富(request, response)
  • 脱离了 XHR,是 ES 规范里新的实现方式

fetch 的缺点:

  • fetch 只对网络请求报错,对 400,500 都当做成功的请求,服务器返回 400,500 错误码时并不会 reject,- 只有网络错误这些导致请求不能完成时,fetch 才会被 reject。
  • fetch 默认不会带 cookie,需要添加配置项: fetch(url, {credentials: 'include'})
  • fetch 不支持 abort,不支持超时控制,使用 setTimeout 及 Promise.reject 的实现的超时控制并不能阻止请- 求过程继续在后台运行,造成了流量的浪费
  • fetch 没有办法原生监测请求的进度,而 XHR 可以

Axios

Axios 是一种基于 Promise 封装的 HTTP 客户端,其特点如下:

  • 浏览器端发起 XMLHttpRequests 请求
  • node 端发起 http 请求
  • 支持 Promise API
  • 监听请求和返回
  • 对请求和返回进行转化
  • 取消请求
  • 自动转换 json 数据
  • 客户端支持抵御 XSRF 攻击

选择使用哪种技术取决于项目的需求:

  • AJAX 适用于老旧项目或对浏览器支持有特殊要求的场景。
  • Axios 更适合需要请求拦截、响应拦截以及较复杂的功能的项目。
  • Fetch 是最简单、最符合现代 JavaScript 风格的方案,适用于现代浏览器且无需额外引入库的项目。

15. 浏览器渲染进程有哪些线程

浏览器的渲染进程主要包含五种线程:

  1. GUI 渲染线程:这个线程负责渲染浏览器页面,解析 HTML、CSS,构建 DOM 树、CSSOM 树、渲染树,以及绘制页面。当界面需要重绘或由于某种操作引发回流时,该线程就会执行。值得注意的是,GUI 渲染线程和 JS 引擎线程是互斥的,当 JS 引擎执行时,GUI 线程会被挂起,GUI 更新会被保存在一个队列中,等待 JS 引擎空闲时立即执行。

  2. JS 引擎线程:也称为 JS 内核,这个线程负责处理 Javascript 脚本程序,解析 Javascript 脚本,并运行代码。JS 引擎线程一直在等待任务队列中任务的到来,然后进行处理。在一个 Tab 页中,无论什么时候都只有一个 JS 引擎线程在运行 JS 程序。

  3. 定时器线程:浏览器定时器(如 setTimeout、setInterval)内部的实现就是由这个线程来控制的。当定时器的时间到达时,会将回调任务推送到任务队列中等待 JS 引擎线程执行。

  4. 事件触发线程:当一个事件被触发时,例如用户点击了一个按钮,这个线程就会将这个事件添加到待处理事件队列的尾部,等待 JS 引擎线程的处理。

  5. 异步 http 请求线程:在 XMLHttpRequest 在连接后(包括请求发出和请求完成),是由这个线程来通知事件触发线程,然后事件触发线程将回调函数添加到待处理事件队列中,等待 JS 引擎线程的执行。

这些线程协同工作,确保浏览器的渲染进程能够高效、准确地处理各种任务和事件。

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

引用计数(Reference Counting)

定义:引用计数(Reference Counting)算法通过跟踪每个对象被引用的次数来确定对象是否为垃圾。 每个对象都有一个引用计数器,引用计数的过程如下:

  • 当一个对象被创建时,其引用计数器初始化为 1。
  • 当该对象被其他对象引用时,引用计数器加 1。
  • 当该对象不再被其他对象引用时,引用计数器减 1。
  • 当引用计数器减至 0 时,意味着该对象不再被引用,可以被垃圾收集器回收。

优势:

实时回收:引用计数可以在对象不再被引用时立即回收,不需要等待垃圾收集器的运行。这可以减少内存占用和提高程序的性能。

简单高效:引用计数是一种简单的垃圾收集算法,实现起来相对容易,不需要复杂的算法和数据结构。

存在的问题:

循环引用:当两个或多个对象相互引用时,它们的引用计数都不为零,即使它们已经不再被其他对象引用,也无法被回收。这导致内存泄漏,因为这些对象仍然占据内存空间,却无法被释放。

计数开销:维护每个对象的引用计数需要占用额外的内存空间,而且每次添加、删除引用都需要更新计数,增加了额外的开销。

标记-清除(Mark and Sweep)

定义:标记-清除(Mark and Sweep)算法通过标记不再使用的对象,然后清除这些对象的内存空间,以便后续的内存分配使用。

它分为两个阶段:标记阶段和清除阶段。

标记阶段:
  • 在标记阶段,垃圾回收器会对内存中的所有对象进行遍历,从根对象开始(通常是全局对象)递归地遍历对象的引用关系。对于每个被访问到的对象,垃圾回收器会给它打上标记,表示该对象是可达的,即不是垃圾。这个过程确保了所有可达对象都会被标记。
清除阶段:
  • 在清除阶段,垃圾回收器会遍历整个内存,对于没有标记的对象,即被判定为垃圾的对象,会被立即回收,释放内存空间。这样,只有被标记的对象会被保留在内存中,而垃圾对象会被清除。
优势:

简单有效:标记-清除算法相对简单,容易实现。它可以准确地找到不再被引用的对象,并回收内存。 处理循环引用:标记-清除算法能够处理循环引用的情况。当对象之间存在循环引用时,即使它们不再被任何其他对象引用,引用计数算法也无法将它们识别为垃圾,而标记-清除算法可以通过遍历的方式找到并清除这些对象。

存在的问题:

垃圾回收过程中的停顿:标记-清除算法会暂停程序的执行,进行垃圾回收操作。当堆中对象较多时,可能会导致明显的停顿,影响用户体验。

内存碎片化:标记-清除算法会在回收过程中产生大量的不连续的、碎片化的内存空间。这可能导致后续的内存分配难以找到足够大的连续内存块,从而使得内存的利用率降低。

17. 说一说从输入 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(全局对象),作用域链,回收机制等)

  • 浏览器通过 DNS 服务器得到域名的 IP 地址,向这个 IP 地址请求得到 HTML 文本
  • 浏览器渲染进程解析 HTML 文本,构建 DOM 树
  • 解析 HTML 的同时,如果遇到内联样式或者样式文件,则下载并构建样式规则,如果遇到 JavaScript 脚本,则会下载执行脚本
  • DOM 树和 CSSOM 构建完成之后,渲染进程将两者合并成渲染树(render tree)
  • 渲染进程开始对渲染树进行布局,生成布局树(layout tree)
  • 渲染树对布局树进行绘制,生成绘制记录

render

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

解析 HTML

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

  • 将 HTML 解析成许多 Tokens
  • 将 Tokens 解析成 object
  • 将 object 组合成一个 DOM 树

解析 CSS

  • 浏览器会从右往左解析 CSS 选择器
  • 我们知道 DOM 树与 CSSOM 树合并成 render 树,实际上是将 CSSOM 附着到 DOM 树上,因此需要根据选择器提供的信息对 DOM 树进行遍历。

19. DOMContentLoaded 与 load 的区别?

  • DOMContentLoaded:仅当 DOM 解析完成后触发,不包括样式表,图片等资源。
  • Load:当页面上所有的 DOM,样式表,脚本,图片等资源加载完毕事触发。

20. 什么是 JavaScript 中的包装类型?

在 JavaScript 中,基本类型是没有属性和方法的,但是为了便于操作基本类型的值,在调用基本类型的属性或方法时 JavaScript 会在后台隐式地将基本类型的值转换为对象,如:

javascript
"abc".length; // 3
"abc".toUpperCase(); // "ABC"

21. DOM 事件中 target 和 currentTarget 的区别

  • event.target 返回触发事件的元素
  • event.currentTarget 返回绑定事件的元素

22.requestAnimationFrame 的优势和使用场景

requestAnimationFrame 是一个用于执行动画和其他视觉效果的 JavaScript 方法。它允许你在浏览器下一次重绘之前执行指定的函数,通常用于创建流畅的动画效果,并且能够更好地与浏览器的渲染流程同步,提高性能。

优势

  1. 高效的动画渲染requestAnimationFrame 是浏览器为动画渲染提供的 API,能够根据屏幕刷新率自动调整更新频率,通常在 60 FPS(每秒 60 帧)时调用,从而使动画更加流畅,避免了过多的绘制和卡顿现象。

  2. 性能优化: 由于它会在浏览器的空闲时间进行渲染,避免了无意义的重复渲染,减少了 CPU 和 GPU 的负担。它还会在浏览器窗口被最小化时停止调用,节省资源。

  3. 同步显示帧requestAnimationFrame 确保动画帧与浏览器的绘制周期同步,使得动画呈现更加平滑,避免了 setTimeoutsetInterval 的不准确性(特别是在不同设备和屏幕刷新率上)。

  4. 自动暂停: 浏览器会自动停止执行不在可见区域内的页面动画,从而节省资源,这对于长时间执行动画的页面尤其重要。

  5. 更精确的时间控制requestAnimationFrame 提供的时间戳(timestamp 参数)比 setTimeoutsetInterval 更精确,可以更好地控制动画的进度。

使用场景

  • 平滑动画:在需要进行连续的动画渲染时,如移动元素、滚动效果、渐变变化等。
  • 游戏开发:在浏览器中创建基于帧的动画(如角色移动、特效)。
  • 图形绘制:在图形绘制应用中,requestAnimationFrame 是进行实时渲染的理想选择。

代码示例

javascript
let animationFrameId;
function animate() {
  // 动画逻辑,如更新位置、样式
  // 更新动画内容

  // 请求下一帧动画
  animationFrameId = requestAnimationFrame(animate);
}

// 启动动画
requestAnimationFrame(animate);

// 取消动画
cancelAnimationFrame(animationFrameId);

Released under the MIT License.