📝前端秋招八股-Ⅱ
网络知识
预检请求
预检请求的主要目的是什么?【单选】
A. 验证客户端的身份
B. 提前获取服务器的响应时间
C. 确定服务器是否在线
D. 检查服务器是否支持特定的 HTTP 方法 ✅
题目解析
- 预检请求(Preflight Request)的主要目的是在跨域请求中,检查服务器是否允许该请求的 HTTP 方法和请求头。浏览器会在实际请求前,通过
OPTIONS
请求方式发送预检请求,以确定目标服务器是否支持该特定的 HTTP 方法(如PUT
、DELETE
)及自定义的请求头,确保安全。 - 通过预检请求,浏览器可以确认服务器是否允许当前的跨域请求,从而保护资源安全。
- 当请求使用了非简单 HTTP 方法(如
PUT
、DELETE
)或带有自定义请求头时,浏览器会自动发起预检请求。
CSRF
以下哪个特征最能表明可能遭受了 CSRF 攻击?【单选】
A. 用户在未进行操作的情况下,发现自己的账户进行了一些敏感操作 ✅
B. 网站响应速度突然变慢
C. 网站出现排版错误
D. 用户收到大量垃圾邮件
题目解析
CSRF(跨站请求伪造)攻击的典型特征是攻击者通过伪造用户的身份,执行用户未授权的敏感操作。这通常在用户已登录某网站的情况下发生。例如,用户点击了一个恶意链接或访问了一个恶意网页,这些页面在用户不知情的情况下向目标网站发送请求,以用户的身份进行操作。
前端代码
display: none
执行以下代码,如果为 box1 盒子设置 display: none
,则下列说法中,正确的是?【多选】
1 | <style> |
A. 即使为 box1 盒子绑定事件,该事件也一定不会被触发
B. box1 不会占据页面的任何空间,但是 box2 仍会占据页面的一定空间
C. 如果为 box2 盒子绑定事件,该事件有可能会被触发 ✅
D. box1 和 box2 将不会占据页面的任何空间 ✅
题目解析
A. 错误。根据下述第 4 点,尽管用户无法通过交互触发事件,但可以通过 JavaScript 的 dispatchEvent
手动触发事件,因此说事件“一定”不会被触发是错误的。
B. 错误。根据第 1、2 和 3 点,display: none
会将元素及其所有子元素(如 box2
)从文档流中移除,并且这些元素不会占据页面的任何空间,因此 box2
也不会占据空间。
C. 正确。和 A 选项类似,虽然 box2
不可见且无法通过用户交互触发事件,但 JavaScript 依然可以手动触发绑定在 box2
上的事件。
D. 正确。根据第 1、2 和 3 点,box1
和 box2
都不会显示在页面上,也不会占据任何空间,因此该选项是正确的。
display: none 的具体效果
display: none
是 CSS 中的一种样式规则,用于完全隐藏一个元素。其具体效果如下:
- 元素不可见:设置
display: none
后,元素不会在页面上渲染。 - 元素完全从文档流中移除:元素不会占据页面的任何空间,页面布局将忽略该元素的存在。
- 所有后代元素也不可见:不仅元素本身不可见,所有子元素也会被隐藏,并且不会占据任何空间。
- 用户无法与元素进行交互:无法通过点击、悬停、聚焦等方式与该元素交互,因此不会触发
click
、hover
、focus
等事件。但是,可以通过 JavaScript 的dispatchEvent
方法手动触发事件。 - 元素仍然存在于 DOM 中:虽然不可见,元素依然存在于 DOM 结构中,因此仍然可以使用 JavaScript 对其进行操作,如修改样式、事件绑定或改变其可见性。
JavaScript 中的类
关于 JavaScript 中的类,下列说法正确的有?【多选】
A. 可以在类中定义静态方法,通过类名直接调用。 ✅
B. 类中的方法默认是公有的。 ✅
C. 类中的构造函数用于初始化实例的属性。 ✅
D. 使用 class 关键字定义类后,可以通过 new 关键字创建类的实例。 ✅
题目解析
A. 正确。 JavaScript 支持在类中定义静态方法,使用 static
关键字定义。静态方法只能通过类名直接调用,而不是通过类的实例调用。
B. 正确。 在 JavaScript 的类中,类的方法默认是公有的(public),可以在实例外部直接访问。如果要定义私有方法,可以使用 #
作为前缀。
C. 正确。 constructor
是类的构造函数,用于在创建实例时初始化实例的属性。它在实例化类时自动调用。
D. 正确。 在 JavaScript 中,使用 class
关键字定义类后,可以通过 new
关键字来创建该类的实例。
flex
以下关于 flex 属性说法正确的是?【多选】
A. flex: 0 0 200px
表示不允许项目放大和缩小,初始宽度为 200px
。 ✅
B. flex: 1 1 auto
等同于 flex-grow:1; flex-shrink:1; flex-basis:auto
。 ✅
C. flex
属性只能应用于块级元素。
D. flex
属性是 flex-grow
、flex-shrink
和 flex-basis
的简写。 ✅
题目解析
C. 错误。 flex
属性不仅可以应用于块级元素,也可以应用于行内元素,只要它们的父元素是一个弹性容器(display: flex
或 display: inline-flex
)。
ABD. 正确。 根据下述对 flex
属性的分析可知。
flex 属性
flex
属性是 CSS 中用于控制弹性盒子中的 伸缩项目 如何 分配空间 的复合属性。它结合了三个子属性:flex-grow
、flex-shrink
和 flex-basis
,用于定义 伸缩项目 在弹性盒子中的 放大、缩小 行为以及 基准大小。
-
flex-basis
:定义了伸缩项目在主轴方向上的基准大小。浏览器通过该属性计算主轴上是否还有多余空间,从而对伸缩项目进行放大和缩小。若flex-basis
设置为auto
,则基准长度默认为伸缩项目的内容宽度(当主轴横向时)或高度(当主轴纵向时)。 -
flex-grow
:控制伸缩项目在主轴方向上的放大行为。假设主轴 剩余 空间为 ,第 个伸缩项目的flex-grow
值为 ,所有伸缩项目的flex-grow
之和为 ,则第 个伸缩项目的拉伸空间为: -
flex-shrink
:控制伸缩项目在主轴方向上的缩小行为。假设主轴 超出 空间为 ,第 个伸缩项目的flex-shrink
值为 ,其flex-basis
值为 ,所有伸缩项目的flex-shrink
与flex-basis
的乘积之和为 ,则第 个项目的收缩空间为:
flex
属性的常用简写如下,
简写 | 全写 | 含义 | 特点 |
---|---|---|---|
initial |
0 1 auto |
只允许压缩,并以伸缩项目的宽高作为基准大小 | 只能压缩 |
auto |
1 1 auto |
允许拉伸和压缩,并以伸缩项目的宽高作为基准大小 | 标准弹性 |
none |
0 0 auto |
不允许拉伸和压缩 | 失去弹性 |
1 |
1 1 0 |
允许拉伸和压缩,但是基准大小为零 (即仅按照 flex-shrink 的值成比例进行压缩,每个伸缩项目的收缩空间为 |
比例弹性 |
注意:基准大小
flex-basis
只影响压缩,不影响拉伸。
font
执行以下代码,下列选项的属性,在 div 中没有定义的是?【多选】 vertical-align text-indent font-size line-height
1 | <style> |
font 属性
font
属性是一个用于设置字体的多个相关样式的复合属性,其完整写法为,
1 | font: font-style? font-varaiant? font-weight? font-size/line-height? font-family |
font-style
字体样式,可取值normal
、italic
、oblique
font-variant
字体变体,可取值normal
、small-caps
font-weight
字体粗细,可取值normal
、bold
、bolder
、lighter
、数值font-size
字体大小,可取数值line-height
行高,可取值normal
、数值font-family
字体族
font
属性中的各个值必须按照特定的顺序出现,其中 font-size
和 font-family
是必须的,对于省略了的某些属性,将使用其默认值。
作用域
执行以下程序,输出结果为?【单选】 undefined 2 抛出异常 1
1 | let a = 1; |
题目解析
箭头函数 f
在定义时捕获了全局变量 a
,即值为 1
。因此,调用 f()
时输出结果是 1
,与 fn
内部的局部变量 a = 2
无关。
width 的特殊取值
执行以下代码,如果想让 div
元素的宽度由文本撑起,并且文本只在一行显示,哪怕最终 div
宽度溢出外部容器,则 ① 式应为?【单选】 max-content min-content fit-content stretch
1 | <style> |
width 的特殊取值
width 属性的取值 | 含义 |
---|---|
max-content |
元素宽度设置为内容宽度的最大值,亦即内容全部在一行时的宽度 |
min-content |
元素宽度设置为内容的最小不可换行宽度,通常为最长单词或不可分割内联元素的宽度 |
fit-content |
元素宽度设置为**max-content 和容器宽度的自适应**,亦即二者取小 |
stretch |
元素宽度设置为容器宽度。 |
parseInt
以下代码的运行结果是?【单选】 2 10 15 25
1 | console.log(parseInt("10+15",2)); |
题目解析
parseInt
是 JavaScript 的一个内置函数,用于将给定的字符串解析为指定基数(即进制)的整数。其语法为,
1 | parseInt(string[, radix]); |
string
必须,表示要解析的字符串。解析字符串时会忽略前导空格,直到遇到无法转换为数字的字符为止。如果字符串无法被解析为一个有效的整数,则返回NaN
。radix
可选,表示要转换的基数(即进制),可取值 2 到 36,默认是 10。
因此,parseInt("10+15", 2)
以二进制解析字符串 "10+15"
,根据所描述的解析规则,可知解析结果为二进制数 10
,打印出来的也就是十进制的 2
。
this
观察以下代码,推测输出
(1) 处执行输出:vscode:10 undefined;Chrome:10 10
(2) 处执行输出:vscode:10 undefined;Chrome:10 10
1 | var a = 10; // 1. |
题目解析
-
this
在不同运行环境中的行为-
浏览器环境:在非严格模式下,
this
指向window
对象。 -
Node.js 环境:在模块作用域中,
this
指向module.exports
。但在函数内部,this
在非严格模式下指向globalThis
,也就是global
对象。
-
-
this
在不同调用方式下的表现-
普通函数调用:
this
指向window
(浏览器)或globalThis
(Node.js)。 -
构造函数调用:
this
指向新创建的实例对象,不依赖环境。
-
-
浏览器中的执行情况
(1)
myFunc()
作为普通函数调用:this
指向window
,因此this.a = 10
将a
设置为全局对象的属性。- 第一层
setTimeout
的箭头函数(3.
处)中的this.a
指向window.a
,输出10
。 - 第二层
setTimeout
的普通函数(4.
处)中的this
指向window
,再次输出window.a
的值10
。
(2)
var fun1 = new myFunc();
作为构造函数调用:this
在myFunc
中指向新创建的实例对象fun1
。- 第一层
setTimeout
的箭头函数继承了this
,指向fun1
,因此console.log(this.a)
输出10
。 - 第二层
setTimeout
的普通函数(4.
处)中的this
指向window
,输出window.a
的值10
。
-
Node.js 中的执行情况
-
在 Node.js 中,由于
var
声明的a
不会自动成为globalThis
的属性,因此globalThis.a
未定义: -
(1)
myFunc()
作为普通函数调用:this.a = 10
并未将a
添加到globalThis
。- 第一次
setTimeout
中的箭头函数输出this.a
,因为此时this
指向globalThis
,globalThis.a
为undefined
。 - 第二层
setTimeout
中的普通函数this
指向globalThis
,因此输出undefined
。
-
(2)
new myFunc()
构造调用:- 第一层
setTimeout
中的this
指向新创建的实例对象fun1
,因此console.log(this.a)
输出10
。 - 第二层
setTimeout
的普通函数this
指向globalThis
,因此globalThis.a
为undefined
。
- 第一层
-
事件循环
观察以下代码,推测输出
1 | let promise2 = new Promise((resolve) => { |
输出结果
嵌套代码
观察以下代码,推测输出
1 | function fun(n, o) { |
输出结果
具名函数表达式
1 | /* |
delete
1 | /* |
提升
1 | /* |
数组加法
1 | const res = [1, 0] + [0, 1]; |
属性描述符、Symbol、与 Object 静态方法
1 | /* |
flex-shrink
1 |
|
前端知识
javascript 是否会阻塞页面渲染
从事件循环的角度来看,JavaScript 的执行模型是单线程的,这意味着 JavaScript 代码的执行是按顺序进行的,而浏览器的渲染也是由同一个主线程负责的。因此,当 JavaScript 代码在执行时,它会阻塞主线程,导致浏览器无法进行页面的解析和渲染。解决方式如下
-
script
标签使用defer
属性-
浏览器会在解析完整个 HTML 文档后,按照顺序异步加载和执行这些脚本。
-
脚本会在 DOM 解析完成后执行,但在
DOMContentLoaded
事件之前。DOMContentLoaded
是浏览器中一个非常重要的事件,它在 HTML 文档被完全加载和解析完成后触发,而不必等待样式表、图片和子框架的完全加载。
-
-
script
标签使用async
属性-
浏览器会异步加载脚本并在加载完成后立即执行,不会等待其他脚本的加载或执行。
与
defer
的区别- 不保证
script
的执行顺序 script
会在下载完成后立即执行,可能会阻塞渲染,但是不会阻塞后序资源的加载
- 不保证
-
-
将
<script>
标签放在<body>
标签的末尾:这样浏览器可以先解析和渲染页面的主体内容,再加载和执行脚本,从而避免页面加载时的白屏或卡顿现象。
浏览器的缓存机制
-
强缓存:强缓存策略通过
expires
或cache-control
响应头进行控制。-
expires
:指定了资源过期的绝对时间。在此之前,浏览器可以直接从缓存中读取资源,而不需要向服务器发送请求。 -
cache-control
:可以通过max-age=xxx
指定资源过期的相对时间;通过public
或private
指定缓存对象,前者表示客户端和代理服务器都可以缓存,后者表示只有客户端才能缓存;通过no-store
指定不缓存任何内容;通过no-cache
表示每次使用缓存都需要经过协商缓存来决定,即每次使用缓存都要验证,强缓存 expires
cache-control
http 版本 http/1.0 http/1.1 优先级 低 高 时间值 绝对 相对
-
-
协商缓存:当浏览器请求强制缓存中的缓存数据发现失效时,会使用协商缓存向服务器确认缓存数据是否仍然有效。协商缓存通过
last-modified
响应头 &if-modified-since
请求头或etag
响应头 &if-not-match
请求头进行控制。-
last-modified/if-modified-since
:服务器在响应头中返回资源的最后修改时间Last-Modified
,浏览器在后续请求中通过If-Modified-Since
发送上次获取的时间,服务器根据资源是否修改决定返回304 Not Modified
或更新的资源。 -
etag/if-not-match
:服务器为每个资源生成一个唯一的标识符ETag
,浏览器在请求时通过If-None-Match
发送该标识符,服务器比较标识符决定返回304 Not Modified
或新的资源。强缓存 last-modified/if-modified-since
etag/if-not-match
优先级 低 高
-
-
缓存的存放位置
缓存位置 内存缓存 memory
磁盘缓存 disk
资源类型 短期的、会频繁访问的 长期的、不频繁变化的 资源举例 页面脚本、样式 图片、文件 读取速度 快 慢 关闭/刷新浏览器是否可保留 否 是
http 常见状态码
-
状态码分类
状态码 含义 1xx 信息响应 2xx 成功响应 3xx 重定向响应 4xx 客户端错误响应 5xx 服务器错误响应 -
信息响应
状态码 状态描述 含义 100 Continue 表示客户端应继续请求,如果已完成请求则忽略。 101 Switching Protocols 服务器理解并同意客户端的协议切换请求。 -
成功响应
状态码 状态描述 含义 200 OK 请求成功,并返回所请求的资源(如网页、文件)。 201 Created 请求成功,并且在服务器上创建了新的资源。 202 Accepted 请求已接收,但尚未处理。 204 No Content 请求成功,但不返回任何内容。 -
重定向响应
状态码 状态描述 含义 301 Moved Permanently 请求的资源已被永久移动到新位置,客户端应使用新的 URL 进行访问。 302 Found 请求的资源临时移动到新位置,客户端应继续使用原有的 URL 进行访问。 304 Not Modified 资源未被修改,客户端可以使用缓存版本 -
客户端错误响应
状态码 状态描述 含义 400 Bad Request 请求有误,服务器无法理解请求。 401 Unauthorized 请求未授权,客户端需要提供认证信息。 403 Forbidden 服务器拒绝请求,即使认证成功也不允许访问。 404 Not Found 请求的资源在服务器上未找到。 405 Method Not Allowed 请求的方法(如 GET, POST)不被允许。 429 Too Many Requests 客户端发送的请求过多,触发了速率限制。 -
服务端错误响应
状态码 状态描述 含义 500 Internal Server Error 服务器遇到错误,无法完成请求。 501 Not Implemented 服务器不支持请求的功能。 502 Bad Gateway 服务器作为网关或代理时,从上游服务器接收到的响应无效。 503 Service Unavailable 服务器当前无法处理请求,通常是因为过载或维护。 504 Gateway Timeout 服务器作为网关或代理时,未及时从上游服务器获得响应。
如何判断数组数据结构
-
instanceof
instanceof
运算符用于检测构造函数的prototype
属性是否出现在某个实例对象的原型链上。1
2
3/* object instanceof constructor */
const arr = [1, 2, 3];
console.log(arr instanceof Array); // true使用
instanceof
检查某个对象是不是数组的方式并不安全,原因如下,-
跨窗口或跨框架环境:
instanceof
检查对象的原型链,验证对象是否继承自Array.prototype
。然而,在不同的 JavaScript 执行环境(如不同的浏览器窗口或框架)之间,Array
构造函数是不同的。1
2
3
4let iframe = document.createElement('iframe');
document.body.appendChild(iframe);
let arr = new iframe.contentWindow.Array(); // 从 iframe 创建的数组
console.log(arr instanceof Array); // false -
instanceof
依赖于原型链:如果你改变了对象的原型链,instanceof
也会受到影响。1
2
3let arr = [];
Object.setPrototypeOf(arr, Object.prototype);
console.log(arr instanceof Array); // false
-
-
constructor
因为实例的
constructor
属性指向其构造函数,那么可以通过判断该属性是否等于Array
来判断实例是否为数组对象。1
2const arr = [1, 2, 3];
console.log(arr.constructor === Array); // true但是这种方式也不安全,原因同
instanceof
-
Object.prototype.toString.call
Object.prototype.toString.call(对象)
方法返回一个表示该对象的字符串。1
2const arr = [1, 2, 3];
console.log(Object.prototype.toString.call(arr) === "[object Array]"); // true -
Array.isArray
Array.isArray(对象)
方法判断一个对象是不是数组对象。(最推荐)1
2const arr = [1, 2, 3];
console.log(Array.isArray(arr)); // true
浏览器端的事件循环机制,常见的宏任务与微任务,代码输出推测
-
执行栈 & 任务队列
-
执行栈
-
JavaScript 是单线程语言,所有的同步任务都会在主线程上的执行栈中依次执行。
-
当一个函数被调用时,它会被压入执行栈中,函数执行完毕后,会从栈中弹出。
1
2
3
4
5
6
7
8
9
10
11function foo(b) {
let a = 10;
return a + b + 11;
}
function bar(x) {
let y = 3;
return foo(x * y);
}
console.log(bar(7));- 调用
bar
时,第一个帧被创建并压入栈中,帧中包含了bar
的参数和局部变量。 - 当
bar
调用foo
时,第二个帧被创建并被压入栈中,放在第一个帧之上,帧中包含foo
的参数和局部变量。 - 当
foo
执行完毕然后返回时,第二个帧就被弹出栈(剩下bar
函数的调用帧)。 - 当
bar
也执行完毕然后返回时,第一个帧也被弹出,栈就被清空了。
- 调用
-
-
任务队列
-
异步任务(如事件监听器、定时器、HTTP 请求等)的回调函数会被放入相应的任务队列中,分为 “宏任务队列” 和 “微任务队列”。
-
宏任务(Macro Task)包括
script
、setTimeout
、setInterval
、setImmediate(Node.js)
、I/O
操作、用户交互(如移动鼠标)、UI 渲染等。 -
微任务(Micro Task)包括
Promise.then
、MutationObserver
、process.nextTick(Node.js)
等。-
调用
Promise.resolve()
或Promise.resolve()
时会创建微任务 -
MutationObserver
可以用来监视DOM
的变化,包括属性的变更、节点的增加、内容的改变等。
-
-
-
-
事件循环
Step1. 执行同步代码,直到执行栈为空
Loop:
Step2. 执行微任务队列中的所有微任务
Step3. 执行宏任务队列中的一个宏任务
- 以上所有步骤都可能产生宏任务与微任务
- 执行微任务时产生的微任务不会推迟到下一个循环执行,而是在当前循环中继续执行
- 如果一项任务执行花费的时间过长,浏览器将无法执行其他任务,例如处理用户事件。因此,在一定时间后,浏览器会抛出一个如 “页面未响应” 之类的警报,建议你终止这个任务。这种情况常发生在有大量复杂的计算或导致死循环的程序错误时。
- 函数
queueMicrotask(func)
用于对func
进行排队,使其微任务队列中执行。
-
代码输出推测
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23console.log(1); // Step1. 同步代码
new Promise((resolve, reject) => {
console.log(2); // Step2. 同步代码
setTimeout(() => { // Step4. 第一个宏任务
console.log(3);
resolve(false);
}, 0);
console.log(4); // Step3. 同步代码
setTimeout(() => { // Step6. 第二个宏任务
reject(true); // 什么也没做,因为第一个宏任务已经改变了 Promise 的状态
console.log(5);
}, 0);
})
.then((res) => console.log(6)) // Step5. 第一个宏任务执行时添加的微任务
.catch((err) => console.log(7));
setTimeout(() => { // Step7. 第三个宏任务
console.log(8);
}, 0);
Promise 的使用,优缺点
-
Promise 是什么?
-
从语法上说,Promise 是一个构造函数;
-
从功能上说,Promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
-
-
Promise 状态
pending
(初始状态)、fulfilled
(操作成功)、rejected
(操作失败)-
创建 Promise 实例时,PromiseState 为
pending
,当调用resolve()
后,变为fulfilled
,当调用reject()
后,变为rejected
-
PromiseState 只能由 pending 变为 fulfilled 或由 pending 变为 rejected 这两种类型
-
一个 Promise 对象的状态只能改变一次(pending => fulfilled/rejected)
-
-
Promise 构造函数
const p = new Promise(executor)
executor
是一个回调函数,其结构为(resolve, reject) => {}
executor
会在 Promise 内部立即 “同步调用”,而异步操作在执行器中执行resolve
是一个在executor
函数体中 “定义成功” 时调用的函数,结构为 value => {},用于将 promise 对象的状态设置为 “成功”reject
是一个在executor
函数体中 “定义失败” 时调用的函数,结构为 reason => {},用于将 promise 对象的状态设置为 “失败”
-
Promise 实例方法
-
p.then(onResolved, onRejected))
-
onResolved
是一个函数,结构为 value => {},该函数在 promise 对象的状态为 ”成功“ 时调用 -
onRejected
是一个函数,结构为 reason => {},该函数在 promise 对象的状态为 ”失败“ 时调用
-
-
p.catch(onRejected)
onRejected
是一个函数,结构为 reason => {},该函数在 promise 对象的状态为 ”失败“ 时调用 -
p.finally(callback)
callback
回调函数,不接受任何参数,该函数在 promise 对象的状态 “兑现”(即非 pending)时调用
-
-
Promise 静态方法
注:这里的 promises 参数是指 Promise 对象构成的数组;同时静态方法的返回值也是一个 Promise 对象
-
Promise.all(promises)
-
如果 Promise 数组中的每个 Promise 对象的状态都为 fulfilled,则 (1) 返回的 Promise 对象的状态为 fulfilled (2)
返回的 Promise 对象的结果为 Promise 数组中每个 Promise 对象成功结果构成的数组
-
如果 Promise 数组中至少一个 Promise 对象的状态为 rejected,则 (1) 返回的 Promise 对象的状态为 rejected (2) 返回的 Promise 对象的结果为 Promise 数组中第一个失败的 Promise 对象的结果
-
-
Promise.race(promises)
返回的 Promise 对象,其状态和结果由 Promise 数组中最先改变状态的 Promise 对象决定
-
Promise.allSettled(promises)
返回的Promise 对象的状态永远不可能是失败(rejected),当 Promise 对象数组中的每一个 Promise 对象的异步操作都结束(不论成功还是失败),这个返回的 Promise 对象的状态变为成功(fulfilled),结果为 Promise 数组中每个 Promise 对象的状态和结果构成的对象构成的数组(如果数组元素 item.status === ‘fulfilled’,此时需要通过 item.value 访问结果;item.status === ‘rejected’,此时需要通过 item.reason 访问结果)
-
Promise.any(promises)
- 只要 Promise 对象数组中任何一个 Promise 对象的状态变为成功(fulfilled),返回的 Promise 对象的状态为成功,并且结果为
- 如果 Promise 对象数组中所有的 Promise 对象的状态都变为失败(rejected),返回的 Promise 对象的状态变为失败,并且结果为一个 AggregateError 实例,其中包含了 Promise 对象数组中所有失败的 Promise 对象的结果(reason)
-
Promise.resolve(value)
- 如果传入的参数为非 Promise 对象,则返回的 Promise 对象的状态为
fulfilled
,结果为参数值 - 如果传入的参数为 Promise 对象,则返回的 Promise 对象的状态和结果与传入的 Promise 对象相同
- 如果传入的参数为非 Promise 对象,则返回的 Promise 对象的状态为
-
Promise.resolve(value)
将传入的参数转换为 Promise 对象,返回的 Promise 对象的状态始终为
rejected
,结果为传入的参数
-
-
Promise 的优缺点
-
优点
-
简化异步编程,避免回调地狱
-
链式调用,允许多个异步操作按次序执行
.then
-
异常穿透,允许对链式调用中的错误进行统一处理
.catch
(更好的错误处理方式)
-
-
缺点
-
无法取消,一旦新建 Promise 就会立即执行,无法中途取消
-
调试困难,错误堆栈不如同步代码直观,难以追踪问题的来源
-
-
Node.js 和浏览器 JavaScript 的区别
区别 | Node.js | 浏览器 |
---|---|---|
运行环境 | 服务器端(访问底层操作系统的功能, 如网络、文件系统等) |
客户端(操作 DOM、用户交互等) |
⭐ 版本环境 | 由开发者决定 | 由访问者决定 |
⭐ 全局对象 | global |
window |
全局变量 | process 、__dirname 、module.exports 等 |
document 、localStorage 、window.location 等 |
⭐ 模块系统 | CommonJs + ESM(ES6 模块系统) | ESM(ES6 模块系统) |
I/O 操作 | 通过文件系统 | 通过网络请求或 Web APIs(如 localStorage ) |
事件循环 | 管理异步操作 | 管理异步操作、DOM 事件、用户交互 |
多线程 | 单线程运行,通过 work_threads 进行多线程 | 单线程运行,通过 web workers 进行多线程 |
关于在 node.js 环境下使用模块化的标注
.js
文件:如果 package.json 的 type 字段为 module,则使用 ESM,否则使用 CommonJs.mjs
文件:使用 ESM.cjs
文件:使用 CommonJs
行内元素、行内块元素、块级元素的区别
区别 | 行内元素 inline | 行内块元素 | 块级元素 block |
---|---|---|---|
⭐ 独占一行 | × | × | √ |
可包含内容 | 文本或行内元素 | 文本或行内元素 | 任意类型的元素 |
⭐ 可设置宽高 | × | √ | √ |
⭐ 默认尺寸 | 宽高由内容决定 | 宽高由内容决定 | 默认占满父容器宽度,高由内容决定 |
⭐ 内/外边距是否有效 | ×(仅水平方向有效) | √ | √ |
常见元素 | a、span、em、strong、 img、input、label |
img、input、button | div、p、h1-h6、ul、ol、li、 section、article、header、footer |
a
元素是例外,其可以包含块级元素,但是不能再包含一个a
元素
display: none 和 visibility: hidden 的区别
特性 | display: none |
visibility: hidden |
---|---|---|
是否占据布局空间 | ×(元素从页面布局中移除) | √(元素占据原位置) |
是否渲染在页面 | ×(元素不被渲染) | √(元素隐藏但保留位置) |
⭐是否影响文档流 | √(移除元素会重新调整文档流,会触发重排) | ×(文档流不变,元素依然占据空间) |
事件是否响应 | × | √ |
⭐是否存在 DOM 树 | √ | √ |
适用场景 | 完全移除元素,如条件渲染,性能优化 | 需要隐藏元素但保持布局,如遮挡效果或过渡 |
OSI 七层模型
OSI 将计算机网络分了七层,每一层抽象底层的内容,并遵守一定规则。基于OSI 网络模型,网络中的节点/物理设备得以进行通信。从顶层到底层,OSI 共规定了以下七层,
- 应用层 Application Layer:支持用户程序使用的服务。应用层包含若干协议:文件传输协议 FTP、安全壳协议 SSH、简单邮件传输协议 SMTP、因特网消息访问协议 IMAP、域名服务 DNS、超文本传输协议 HTTP
- 表示层 Presentation Layer:数据格式化(如字符编码控制)、数据加密(SSL/TLS)。
- 会话层 Session Layer:建立、维持和终止两个用户程序之间的连接。会话层以上的网络层关注:如何与用户应用程序建立连接,以及如何向用户展示数据。
- 传输层 Transport Layer:通过 TCP 或 UDP 将数据发送给设备的特定端口,建立数据通信。传输层有两个重要协议,传输控制协议 TCP 和用户数据报协议 UDP。
- 传输控制协议 TCP:面向连接的协议;优先保证数据质量而不是传输速度
- 用户数据报协议 UDP:无连接的协议;优先保证传输速度而不是传输质量
- 网络层 Network Layer:通过路由器在网络间进行通信。
- 数据链路层 Data Link Layer:定义数据的传输格式,进行线路规划与流量控制,进行错误监测与校正。
- 物理层 Physical Layer:通过网线、电缆等方式将设备物理连接。
TCP/IP 四层模型
TCP/IP 四层模型可以看作是对 OSI 七层模型的简化,从顶层到底层,具体划分如下,
- 应用层:对应 OSI 的应用层、表示层、会话层。
- 传输层:对应 OSI 的传输层,通过 TCP/IP 协议实现端口到端口的通信(即两台主机间进程通信)
- 网络层:对应 OSI 的网络层,建立主机间的通信。
- 数据链路层:对应 OSI 的数据链路层、物理层,通过物理手段连接设备。
TCP 和 UDP 的区别
-
TCP(Transmission Control Protocol,传输控制协议)是基于连接的,在传输数据时会建立接收端和发送端之间的连接,并在传输过程中始终保持这种连接。
- TCP 在传输数据时会检查错误,以确保发送的数据完整到达目的地。
- TCP 会根据接收端的容量进行优化和调整传输数据的速度。
- TCP 会确认数据已到达目的地,如果第一次传输失败,会尝试重新传输。
-
UDP(User Datagram Protocol,用户数据报协议)是无连接的,在传输数据时不会在双方之间建立事先的连接。
- UDP 以较小负担发送给数据包,从而减少端到端的延迟。
- 传输过程中数据包丢失数据包,UDP 仍会传送数据,即数据包的丢失不会中断整个传输。
- UDP 可以通过广播和多播功能,同时发送数据给多个接收端。
- UDP 比 TCP 更快速、高效。
-
TCP 和 UDP 都是位于传输层的通信协议,其区别如下,
区别 TCP UDP 可靠性(按顺序、完整性、错误检测) 高 低 连接性 面向连接 无连接 速度 较低 高 … … … -
TCP 和 UDP 在应用层协议的使用场景
TCP UDP SMTP 电子邮件 DNS 域名转换 TELNET 远程终端接入 TFTP 文件传输 HTTP 万维网 SNMP 网络管理 FTP 文件传输 NFS 远程文件服务器
如何封装健壮性强的 react 组件
-
特征:一个好的组件应该具有 ①高内聚、低耦合 ②隐藏内部结构 ③职责单一 这些特点。
- 所谓的高内聚,就是将逻辑紧密相关的内容放在一个组件中;而低耦合,就是将不同组件之间的依赖关系尽量弱化。
- 组件的低耦合有利于修改、替换、复用、单元测试。
- 隐藏内部结构,就是要求仅通过一组
props
来控制组件行为,这也有利于减少组件对其他组件的依赖。 - 职责单一,就是要求一个组件只负责一件事情,这有利于组件的维护和复用。
-
设计:封装一个组件需要思考 ①这个组件是干什么的?②这个组件应该至少需要知道哪些信息?③这个组件会反馈什么内容?,相较于实现需求,更重要的是对需求的抽象。
-
行为:父组件通过
props
的方式向封装好的子组件进行通信,可以使用prop-types
库或 TypeScript 进行参数验证,检查传入组件的参数是否合法(数据类型、是否必传等)。
es6 新特性(ES2015)
-
let
和const
关键字 -
模板字符串
-
箭头函数
-
默认参数、可选参数(又叫其余参数)
1
2const func1 = (message = 'hello') => { /**/ } // 参数默认值
const func2 = (...rest) => { /**/ } // 可选参数,rest 参数是一个数组 -
解构赋值(对象、数组)
-
展开运算符
-
类,通过
class
关键字定义 -
模块化,通过
import
和export
实现 -
Promise
-
set
、weakSet
、map
、weakMap
输入 url 到渲染页面的过程
Step1. 浏览器查找当前 URL 是否存在缓存,并比较缓存是否过期
这里的缓存机制包括强制缓存和协商缓存
强制缓存:当浏览器向服务器发送请求时,服务器返回资源的同时,会使用
Expires
或Cache-Control
响应头来控制是否缓存对应的资源。协商缓存:当浏览器请求强制缓存中的缓存数据时,如果存在缓存数据及其标识,但缓存结果失效,此时浏览器无法确定该缓存数据是否仍然有效。在这种情况下,浏览器会携带对应的资源标识发送 HTTP 请求,以确认缓存的数据是否与服务器上的数据一致。如果数据一致,服务器会返回 304 状态码,告诉浏览器可以继续使用缓存中的数据;如果数据不一致,服务器会返回 200 状态码,并提供最新的数据。
Last-Modified
/Etag
响应头和If-Modified-Since
/If-None-Match
请求头用于控制协商缓存。
Step2. DNS 解析 URL 对应的 IP
Step3. 根据 IP 建立 TCP 连接 —— 三次握手
三次握手的大致流程为
- 客户端发送 syn 包,等待服务器确认。
- 服务器收到 syn 包,同时发送 syn + ack 包。
- 客户端收到 syn + ack 包,向服务器发送 ack 包。
发送完毕后,客户端和服务端进入 established 状态,完成三次握手。
Step4. 发送 HTTP 请求
Step5. 服务器处理请求,并发送 HTTP 响应
Step6. 浏览器解析响应报文并渲染页面
解析报文渲染页面的大致流程为
- HTML -> DOM 树
- CSS -> CSSOM 树
- DOM 树 + CSSOM 树 -> Render 树
- 浏览器开始渲染并绘制页面(这里涉及到回流重绘两个概念:回流涉及到重新渲染,重绘涉及到样式的修改)
Step7. 关闭 TCP 连接 —— 四次挥手
四次挥手的大致流程为
- 客户端发送 fin 包,表示客户端不再发送数据,但可以接收数据
- 服务器收到 fin 包,发送 ack 包及序号,此时服务器处于半关闭状态
- 服务器发送 fin 包,表示关闭服务器到客户端的数据发送
- 客户端收到 fin 包,发送 ack 包并确认序号
经过一段时间后,客户端进入 closed 状态,服务端收到客户端的确认后立即进入 closed 状态并结束 TCP 连接。
怎么减少回流与重绘
- 回流 reflow:元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染。
- 重绘 repaint:元素样式的改变(但宽高、大小、位置等不变)。
- 回流 Vs. 重绘
- 重绘不是很消耗性能,但是回流很消耗性能
- 回流一定触发重绘,但重绘不一定会触发回流
- 减少回流或重绘的方式
- 放弃手动操作 DOM,使用 vue 或 react 等框架,以数据驱动视图
- 批量修改 DOM,减少回流重绘的次数
- DOM 脱离文档流再进行处理
- 合并对 DOM 样式的修改,采用 css class 来修改