面试题:
开始是自我介绍和项目相关讲解。以下帮整理的面试题内容,主要针对个人·(排版比较随意)当然如果你愿意看,我也很乐意分享出来一起学习进步~~
第5题:Promise 的几种状态分别是怎么变化的?
Promise 有三种状态:pending(进行中)、fulfilled(已解决)和 rejected(已拒绝)。当一个 Promise 被创建时,它的初始状态是 pending。然后它会根据执行器函数中的逻辑变为 fulfilled 或 rejected 状态。
当执行器函数中的代码成功执行并调用 resolve() 方法时,Promise 变为 fulfilled 状态。 当执行器函数中的代码抛出错误或调用 reject() 方法时,Promise 变为 rejected 状态。 一旦 Promise 进入了 fulfilled 或 rejected 状态,这个状态就不会再改变。
第6题:
问题: 有一段 promise 代码块,逻辑是 new 一个 promise,function 有两个参数 resolved 和 rejected,执行这个 function 的时候,还没有执行到 resolved 或者 rejected 回调之前,我们的代码发生了报错,下面链式调用了.then.catch.then.catch,会怎么执行?
简单回答: 如果在 Promise 执行过程中发生错误,并且在调用 resolve() 或 reject() 之前,那么该错误会被捕获并在 catch() 中处理。因此,在这种情况下,第一个 .catch() 将被触发,而后续的 .then() 和 .catch() 不会被执行。
怎么理解这个回答?
当一个 Promise 的执行器函数(executor function)在执行过程中抛出了一个错误,但在此之前没有调用 resolve() 或 reject() 方法时,这个错误会被 Promise 自动捕获,并将 Promise 的状态变为 rejected。这意味着,即使错误发生在 resolve() 或 reject() 被调用之前,该 Promise 也会被视为失败,并且链式调用中的第一个 .catch 方法会被触发。
考虑以下代码片段:
new Promise((resolve, reject) => {
console.log('Promise 执行');
throw new Error('执行器中抛出的错误'); // 抛出一个错误
// resolve('成功'); // 这一行永远不会被执行
// reject('失败'); // 这一行也永远不会被执行
})
.then(result => {
console.log('第一个 then: ' + result);
})
.catch(error => {
console.log('第一个 catch: ' + error.message); // 捕获错误
})
.then(result => {
console.log('第二个 then: ' + result);
})
.catch(error => {
console.log('第二个 catch: ' + error.message);
});
输出结果
Promise 执行
第一个 catch: 执行器中抛出的错误
第二个 then: undefined
执行流程解析
- Promise 创建与执行:当
new Promise被调用时,执行器函数立即执行。在这个过程中,console.log('Promise 执行')会被执行,随后抛出一个错误。 - 错误捕获:由于抛出了一个错误,Promise 自动将状态从 pending 更改为 rejected,并将错误传递给最近的
.catch方法。 - 第一个
.catch被触发:因为 Promise 已经处于 rejected 状态,所以第一个.catch方法会被调用,输出'第一个 catch: 执行器中抛出的错误'。 - 第二个
.then的执行:.catch方法本质上也是.then的一种形式,它返回一个新的 Promise,这个 Promise 默认是 resolved 的,除非.catch的回调函数抛出错误或返回一个 rejected 的 Promise。因此,在第一个.catch成功处理了错误之后,链式调用中的下一个.then会被执行,但是它不会接收到任何特别的值,因为它接收到的是.catch方法返回的默认 resolved 状态的 Promise。 - 后续
.catch不会被触发:由于第二个.then没有抛出错误,后续的.catch方法不会被调用。
总结
-
当 Promise 的执行器函数抛出错误时,Promise 会自动变为 rejected 状态。
-
链式调用中的第一个
.catch会捕获这个错误,并处理它。 -
如果
.catch成功处理了错误,那么后续的.then方法会继续执行。 -
如果
.then或.catch中再次抛出错误,它会跳过后续的.then方法,直接寻找下一个.catch方法来处理错误。
第7题:事件循环机制(去看看 w3c 的最新标准)
事件循环是 JavaScript 引擎的核心概念之一,用于管理异步任务的执行顺序。JavaScript 是单线程语言,这意味着在同一时间只能执行一个任务。为了支持异步操作,如网络请求、定时器等,JavaScript 使用事件循环来调度这些任务。
从下面图片进行讲解:
事件循环的基本流程如下:
- 在一个大的宏任务宿主环境下,JavaScript 引擎首先执行同步代码,直到遇到异步任务(如 setTimeout、Promise 等)。
- 异步任务被添加到相应的队列中(例如,setTimeout 添加到计时器队列,Promise 添加到微任务队列)。
- 当当前栈清空后,事件循环从队列中取出下一个任务并执行。
- 如果任务执行完成后还有其他任务等待执行,则重复步骤 3 直至所有任务完成。
第8题:说出代码执行的顺序
console.log(1)
setTimeout(() => {
console.log(2)
}, 0)
new Promise((resolve, reject) => {
console.log(3)
resolve()
}).then(() => {
console.log(4)
})
document.appendChild(p); // p 标签在前面已经定义好了的
最终输出顺序为: 1 3 4 2 (因为 setTimeout 在微任务之后执行)
简单解释一下:
- console.log(1) - 输出 "1"
- setTimeout - 设置一个延迟时间为 0 毫秒的回调函数,但不会立即执行。
- 创建一个新的 Promise 并立即执行其执行器函数:
- console.log(3) - 输出 "3"
- resolve() - 解决 Promise,将其状态设置为 fulfilled
- Promise 的 .then() 处理程序将被添加到微任务队列中。
- document.appendChild(p) - 将元素插入 DOM
- 由于 setTimeout 的延迟时间为 0 毫秒,所以它会在当前宏任务结束后立即执行。但是,由于 Promise 的 .then() 处理程序是在微任务队列中,它将在 setTimeout 完成后立即执行。
第9题:输入URL后发生了什么?
当你在浏览器地址栏输入一个URL并按下回车键后,会发生一系列复杂的操作,这些操作大致可以分为以下几个步骤:
-
URL解析:浏览器首先解析输入的URL,提取出协议(如HTTP或HTTPS)、域名、端口号(如果指定)、路径以及查询参数等信息。
-
DNS域名解析:浏览器会尝试从本地缓存中查找域名对应的IP地址。如果找不到,它会查询操作系统的DNS缓存,如果还是找不到,会向本地DNS服务器发送查询请求。本地DNS服务器会通过递归或迭代查询的方式,最终
找到目标域名的IP地址并返回给浏览器。 -
建立TCP连接:浏览器使用获取到的IP地址,通过
三次握手与服务器建立TCP连接。这个过程确保了数据传输的可靠性。 -
发送HTTP请求:一旦TCP连接建立,浏览器会发送一个HTTP请求到服务器。请求中包含请求方法(如GET或POST)、请求头(包括User-Agent、Accept等信息)以及请求体(如果适用)。
-
服务器处理请求并返回响应:服务器接收到请求后,会解析请求头和请求体,根据请求的内容进行处理。处理完成后,服务器会
生成一个HTTP响应,包含状态码、响应头和响应体(如HTML文档、图片等),并通过已建立的TCP连接返回给浏览器。 -
浏览器接收响应并解析渲染页面:浏览器接收到服务器的响应后,会根据响应头的信息判断响应类型,并解析响应体。对于HTML文档,
浏览器会构建DOM树和CSSOM树,然后结合这两个树构建渲染树,计算布局并绘制页面。在这个过程中,浏览器可能会发现需要加载更多的资源(如CSS文件、JavaScript文件、图片等),会发起新的请求来加载这些资源。 -
断开TCP连接:当所有的资源加载完成并且页面渲染完毕后,浏览器会根据HTTP响应头中的
Connection字段决定是否断开TCP连接。如果Connection字段设置为keep-alive,则连接会保持打开状态以供后续请求使用;否则,连接会被断开。
第10题:简述浏览器内核的作用
浏览器内核是浏览器的核心组件,负责渲染网页内容并处理网页上的交互。浏览器内核的主要职责包括:
- 解析HTML、CSS和JavaScript:将这些代码转换为浏览器可以理解和显示的内容。
- 构建DOM树和CSSOM树:解析HTML生成DOM树,解析CSS生成CSSOM树。
- 布局计算:根据渲染树计算每个节点的位置和大小,确定页面的布局。
- 绘制页面:将计算好的布局信息转换为屏幕上的像素,显示在用户的浏览器窗口中。
- 网络通信:管理与服务器之间的通信,包括发起请求、接收响应、处理重定向等。
不同的浏览器可能使用不同的内核,例如Chrome和Edge使用Blink(基于WebKit),Firefox使用Gecko,而Safari则使用WebKit。每个内核都有自己的特点和优化策略。
浏览器内核 对比 JS引擎
JavaScript引擎是浏览器内核的一部分,专门负责解析和执行JavaScript代码。具体职责包括:
- 解析JavaScript代码:将JavaScript代码转换为可执行的字节码或机器码。
- 执行JavaScript代码:运行解析后的代码,实现网页的动态效果和交互功能。
- 内存管理:管理JavaScript代码中使用的内存,包括垃圾回收等。
浏览器内核 :网络通信功能
- 发起请求:当用户输入URL或点击链接时,浏览器内核会解析URL并发起HTTP或HTTPS请求,请求特定的资源。
- 接收响应:浏览器内核接收服务器返回的HTTP响应,解析响应头和响应体,处理返回的数据。
- 处理重定向:如果服务器返回的响应中包含重定向信息(如HTTP状态码301或302),浏览器内核会自动处理这些重定向,发起新的请求。
- 管理连接:浏览器内核管理与服务器之间的TCP连接,包括建立、维护和断开连接。
期待下一篇吧!