常见问题
运算符以及顺序问题
题 1
var x, y=0; x=10; y=x++;
运行以上程序后,y的值为()
分析 y = x++
:
- 这个表达式中,
x++
是后置递增运算符。 y = x++
的含义是:- 先将
x
当前的值(即10
)赋给y
,然后再对x
进行递增。
- 先将
- 所以,
y
将会被赋值为10
,然后x
的值会递增到11
。
所以 y
的值为 10
。
题 2
var a = 1 + (1>2)?1:2; a = ?
运算符优先级:
- 加法运算符
+
的优先级高于三元运算符? :
。 - 因此,
1 + (1 > 2)
会先执行,然后结果会参与三元运算符的判断。
分析步骤:
(1 > 2)
计算结果是false
,在 JavaScript 中,false
转换为0
。1 + (1 > 2)
就等同于1 + 0
,结果为1
。- 所以,代码实际上变成了:
var a = 1 ? 1 : 2;
- 在 JavaScript 中,
1
是一个真值(truthy),因此三元运算符会选择1
。
题 3
var a=3;var b=4; a=a^b; b=b^a; b=?
在这段代码中,使用了按位异或运算符 (^
) :相同为 0,相异为 1。
结果为 3。
typeof null
: object
在 JavaScript 中,typeof null
的结果是 "object"
。这是一个历史遗留的 bug,而不是逻辑上的正确结果。
为了正确检查一个值是否为 null
,应该直接使用严格相等( === )操作符:
let value = null;
if (value === null) {
console.log("Value is null");
}
这样可以准确地判断一个值是否为 null
。
Date 对象
题 1
new Date(2020,12,1).getMonth()
在 JavaScript 中,new Date(2020, 12, 1)
这段代码创建了一个日期对象,但月份参数的范围是从 0
(表示一月)到 11
(表示十二月)。因此,当你传入 12
作为月份参数时,JavaScript 会将日期推进到下一年。
具体来说,new Date(2020, 12, 1)
实际上表示的是 2021 年 1 月 1 日,因为 12
超出了月份范围。然后调用 .getMonth()
方法会返回月份的索引值。
所以返回值为 0 。
理解 HTML,CSS,JS
可以将 HTML、CSS 和 JavaScript 比喻成建造一座房子的过程:
HTML 是房子的结构:
想象一下,HTML 就像是房子的框架或骨架。它定义了房子的基本结构,比如墙壁、屋顶、门窗的位置。HTML 标签就是房子的砖块、梁柱,决定了房子是如何被搭建起来的。CSS 是房子的装修:
如果 HTML 是房子的框架,那么 CSS 就是房子的外观和内饰。CSS 决定了房子的颜色、材质、装饰风格等。它让房子看起来更美观、舒适。比如,CSS 可以控制墙壁的颜色、窗帘的样式,以及家具的摆放。JavaScript 是房子的功能:
JavaScript 就像是房子里的电器设备和智能家居系统。它让房子变得有生命,可以与人互动。比如,JavaScript 可以控制灯光的开关、空调的温度调节,甚至让窗帘自动开关。这使得房子不仅仅是一个静态的结构,而是一个可以响应住户需求的动态空间。
这三者在网页开发中的作用:
- HTML 提供了网页的基本结构。
- CSS 使得网页的外观更美观。
- JavaScript 则赋予网页交互和动态功能。
浏览器渲染网页的过程涉及多个步骤,每个步骤对最终网页的呈现都有重要影响。理解这些步骤可以帮助你优化网页性能,特别是在避免不必要的重绘和重排方面。
浏览器渲染原理
- 解析 HTML 和生成 DOM 树:
浏览器从上到下解析 HTML 文件,并将每个元素转换成 DOM 树的节点。DOM 树(Document Object Model Tree)表示了网页的结构。 - 解析 CSS 和生成 CSSOM 树:
同时,浏览器也会解析 CSS 文件,并生成 CSSOM 树(CSS Object Model Tree),表示所有样式规则和它们如何应用于 DOM 树中的各个元素。 - 生成渲染树(Render Tree):
浏览器将 DOM 树和 CSSOM 树结合,生成渲染树。渲染树包含了每个可见元素的可视化信息,比如样式、大小和位置等。注意,渲染树不会包含如<head>
或display: none
之类的不可见元素。 - 布局(Layout):
布局阶段也叫做“排版”或“回流(Reflow)”,它确定了渲染树中每个节点的确切位置和尺寸。这个过程是基于视口的大小和元素之间的关系来进行的。 - 绘制(Painting):
在这个阶段,浏览器根据渲染树将元素的视觉信息转换为实际的像素。这包括了文本、颜色、边框、阴影等所有样式的应用。 - 合成(Compositing):
对于复杂的页面,浏览器可能会将页面分割成多个图层(Layers)。这些图层会独立渲染,最后再合成到一起,显示在屏幕上。
重绘(Repaint)和重排(Reflow)
重绘(Repaint):
当页面中的某些元素的外观(如颜色、边框、阴影等)发生变化时,但它们的布局和位置没有改变,这时浏览器只需要重新绘制这些元素的像素,而不需要重新计算布局。这种操作称为重绘。重绘的成本相对较低,因为不涉及页面布局的重新计算。
重排(Reflow):
当页面中的某些元素的尺寸、位置、结构等发生变化时,浏览器需要重新计算这些元素和它们周围元素的位置和尺寸。这种操作称为重排,也叫回流。重排的成本较高,因为它可能会影响整个页面的布局,导致更多的元素需要重新布局和绘制。
避免不必要的重绘和重排的技巧
- 尽量减少对 DOM 的频繁操作:
频繁的 DOM 操作会导致大量的重排和重绘。可以通过将多次 DOM 操作合并在一起或者使用文档片段(Document Fragment)来减少影响。 - 避免逐个更改样式:
如果需要更改一个元素的多个样式属性,最好一次性更改,比如通过修改class
或者使用cssText
,而不是一个个属性地修改,这样可以减少重排的次数。 - 使用 CSS 的
will-change
提示:will-change
属性可以提前告诉浏览器哪些元素可能会发生变化,浏览器会为这些元素优化渲染过程,减少重排和重绘的开销。 - 脱离文档流的元素:
使用position: absolute
或position: fixed
让元素脱离文档流,这样它们的变化不会影响其他元素的布局,减少重排的范围。 - 使用
transform
和opacity
:
使用 CSS 的transform
和opacity
属性来执行动画或变换,它们通常只会触发重绘,而不会引起重排,从而减少性能开销。
HTTPS(Hypertext Transfer Protocol Secure)是 HTTP 的安全版本,利用 SSL/TLS 协议为数据传输提供加密和身份验证。HTTPS 的原理涉及以下几个方面:
HTTPS
原理
- 加密通信: - 混合加密,在通信建立前使用非对称加密,在通信建立后使用对称加密。
- 在 HTTPS 中,HTTP 协议与 SSL/TLS(Secure Sockets Layer/Transport Layer Security)协议结合使用,保证数据在客户端和服务器之间的传输是加密的,防止被窃听或篡改。
- 这通过对称加密实现,在握手过程中,客户端和服务器协商出一个共享密钥,用于加密通信内容。
- 身份验证: - 数字证书
- HTTPS 使用数字证书来验证服务器的身份。客户端可以通过证书确保自己连接到的确实是目标服务器,而不是中间人攻击者伪装的服务器。
- 数据完整性: - 摘要算法(数字签名)
- SSL/TLS 使用 消息验证码(MAC) 来确保数据的完整性。如果数据在传输过程中被篡改,接收方会发现数据的 MAC 不匹配,从而识别出问题。
证书的防篡改、防调包机制
- 数字证书的结构:
- 数字证书包含服务器的公钥、证书持有者的信息(如域名)、证书颁发机构(CA)的信息、证书的有效期等。
- 证书由证书颁发机构(CA)使用其私钥签名,这意味着任何篡改都会导致签名无效,从而防止证书被伪造或篡改。
- 证书的防篡改机制:
- 证书颁发机构(CA)使用其私钥对证书内容生成数字签名。当客户端收到服务器的证书时,会用 CA 的公钥验证该签名。如果签名验证失败,说明证书可能被篡改,客户端会拒绝建立安全连接。
- 由于私钥是保密的,只有合法的 CA 才能生成有效的签名,任何篡改都会导致签名失效。
- 防调包机制(防止中间人攻击):
- 当客户端发起 HTTPS 请求时,服务器会返回其数字证书,客户端验证证书的有效性和签名。若证书验证通过,客户端使用证书中的公钥加密一个随机生成的会话密钥(对称密钥),并发送给服务器。
- 只有拥有相应私钥的服务器才能解密这个会话密钥,从而完成安全的对称加密通信。如果中间人试图在此过程中插入自己的证书,由于中间人的证书没有经过受信任的 CA 签名,客户端会检测到并警告用户连接不安全。
- 证书链验证:
- 浏览器通常会验证证书的链条,即从服务器证书向上追溯到根证书。每个证书都由上一级证书签发,最终到达根证书。根证书被预置在操作系统或浏览器中,通常是可信的。通过这种链式验证,确保证书的合法性和安全性。
HTTPS 握手过程
- 客户端请求:
- 客户端(如浏览器)请求与服务器建立 HTTPS 连接,并提供支持的加密算法列表。
- 服务器响应:
- 服务器选择一个加密算法,并发送服务器的数字证书。
- 客户端验证:
- 客户端使用 CA 的公钥验证服务器证书的真实性。如果证书有效,客户端生成一个随机的会话密钥,并用服务器的公钥加密这个密钥,然后发送给服务器。
- 加密通信:
- 服务器用私钥解密会话密钥,之后的通信都使用这个对称密钥加密,从而保证数据传输的安全性。
虚拟 DOM
虚拟 DOM(Virtual DOM, VDOM) 是一种编程概念和技术,用于优化 UI 渲染性能。它通过在内存中构建一个虚拟表示(通常是以 JavaScript 对象形式)来跟踪实际 DOM 的状态,并在 UI 发生变化时有效地更新真实 DOM。
VDOM 的工作原理
- 创建 VDOM:
当 UI 需要被渲染时,首先会在内存中生成一个虚拟 DOM 树。这个虚拟 DOM 树是使用 JavaScript 对象来描述实际 DOM 树的结构和属性。 - VDOM 更新:
当状态或数据发生变化时,新的 VDOM 树会被生成。然后,框架会将这个新树与旧的 VDOM 树进行比较(通常使用“Diffing”算法),找出变化的部分。 - 更新真实 DOM:
框架会基于 VDOM 的差异计算出最小的必要操作,将这些操作应用到实际 DOM 上,从而更新浏览器显示的内容。
VDOM 与原生 DOM 的对比
- 性能优化:
- 原生 DOM: 原生 DOM 操作直接与浏览器的渲染引擎交互,每次操作都会触发 DOM 更新和重绘、重排。这种操作在大量元素或频繁操作时会非常耗时,导致性能问题。
- 虚拟 DOM: VDOM 通过在内存中进行计算,将多次小的 DOM 变更合并为一次大的更新,再将这一更新批量应用到真实 DOM 上。这样就减少了浏览器的重排和重绘次数,从而提升了性能。
- 简化开发:
- 原生 DOM: 直接操作 DOM 需要开发者手动跟踪每个元素的状态,维护复杂的 DOM 结构,容易出现错误,尤其是在大型应用中。
- 虚拟 DOM: VDOM 让开发者可以专注于编写声明式的 UI 代码,而无需关注底层的 DOM 操作细节。框架(如 React)负责在状态改变时自动更新 UI,使代码更简洁、更易于维护。
- 跨平台能力:
- 原生 DOM: 是浏览器特有的技术,直接操作依赖于浏览器的 API,难以移植到非浏览器环境中。
- 虚拟 DOM: 是一种抽象的概念,不依赖具体的平台。因此,它可以用于跨平台应用开发,比如 React Native 中使用虚拟 DOM 来生成原生移动应用的组件。
为什么要用 VDOM
- 性能提升:
VDOM 通过减少直接 DOM 操作的次数和范围,提高了应用的渲染性能。尤其在复杂 UI 场景下,VDOM 的性能优势更为明显。 - 开发体验:
VDOM 支持声明式编程,使 UI 的开发和管理变得更加简单和直观。开发者可以用更少的代码、更高层次的抽象来管理复杂的 UI 状态。 - 一致性和可维护性:
VDOM 的更新逻辑是由框架内部自动处理的,减少了手动操作 DOM 可能带来的 bug 和不一致性,使代码更加可靠和可维护。
异步问题
Promise:解决的问题
在 JavaScript 中,异步操作(如网络请求、文件读写、计时器等)可能导致“回调地狱”(Callback Hell)问题。回调地狱是指多层嵌套的回调函数,导致代码难以阅读和维护。
Promise 是 JavaScript 提供的一种更优雅的方式来处理异步操作。它是一个对象,代表一个尚未完成但承诺未来会完成的操作,并最终返回一个值(成功的结果或失败的原因)。
Promise 解决了以下问题:
- 更清晰的异步流程控制:
使用链式调用(then
)可以避免深层嵌套的回调函数,使代码更容易理解。 - 错误处理:
Promise 提供了统一的错误处理机制,通过.catch()
捕获异步操作中的错误,避免了在每个回调函数中手动处理错误。 - 并行执行异步操作:
Promise.all() 等方法允许并行执行多个异步操作,并在所有操作完成后执行下一步操作。
示例:
// 回调地狱的示例
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
// 使用 Promise
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log('Final result: ' + finalResult))
.catch(failureCallback);
async/await:进一步优化异步代码
虽然 Promise 改善了异步代码的可读性,但仍然依赖于链式调用,代码仍然有些复杂。为了解决这个问题,JavaScript 引入了 async/await,它是基于 Promise 的语法糖,使得异步代码看起来像是同步代码。
- 更简洁、更易读:
async/await 让异步代码写起来像是同步代码,没有复杂的.then
链式调用,使得代码结构更清晰。 - 更好的错误处理:
async/await 允许使用try/catch
来捕获错误,这种方式比 Promise 的.catch()
更直观,更符合传统的同步错误处理习惯。
示例:
// 使用 Promise
function getData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log(data);
})
.catch(error => {
console.error('Error:', error);
});
}
// 使用 async/await
async function getData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Error:', error);
}
}
没有这些之前如何处理异步操作
在 Promise 和 async/await 出现之前,JavaScript 的异步操作主要依赖于 回调函数。回调函数是异步操作完成后执行的函数,但这种方式容易导致以下问题:
- 回调地狱(Callback Hell):
由于回调函数可能嵌套多层,导致代码结构混乱、难以维护。 - 错误处理复杂:
需要在每个回调中手动处理错误,并传递给下一个回调,容易遗漏错误处理。
示例:
function doSomething(callback) {
setTimeout(() => {
// 异步操作
callback(null, 'result');
}, 1000);
}
doSomething(function(error, result) {
if (error) {
console.error('Error:', error);
} else {
console.log('Result:', result);
}
});
宏任务与微任务
宏任务(Macro Tasks)
宏任务(也称为任务或事件任务)是一个较大的执行单元,通常包括以下类型的任务:
setTimeout
:定时器任务。setInterval
:定期执行的任务。setImmediate
(Node.js):在当前事件循环周期结束时执行的任务。- I/O 操作(如文件读写、网络请求等)。
- UI 渲染:如页面重排和重绘。
宏任务队列中的任务是一次执行一个的,每当执行完成后,事件循环会继续从宏任务队列中取出下一个任务执行。
微任务(Micro Tasks)
微任务是一个较小的执行单元,通常包括以下类型的任务:
Promise
的then
和catch
回调:在 Promise 解决后立即执行的回调。MutationObserver
:观察 DOM 变动的回调。process.nextTick
(Node.js):在当前阶段执行的任务。
微任务队列中的任务会在当前宏任务完成后,立即执行所有微任务,然后才会继续处理下一个宏任务。
任务队列的执行顺序
- 宏任务队列:事件循环从宏任务队列中取出一个任务执行。宏任务包括代码执行、定时器、I/O 等。
- 微任务队列:在当前宏任务执行完成后,事件循环会立即处理所有的微任务。微任务包括 Promise 的回调和 MutationObserver 的回调。
- 渲染和更新:在执行完所有微任务后,浏览器可以进行 UI 更新和重绘操作。
- 继续下一个宏任务:事件循环再次从宏任务队列中取出下一个任务执行,重复上述过程。
状态码
HTTP 状态码用于表示服务器对客户端请求的处理结果。它们分为五类,每类都有不同的含义。以下是一些常见的 HTTP 状态码及其说明:
1xx 信息性状态码
这些状态码表示请求已被接受,继续处理。
- 100 Continue:表示服务器已收到请求的初步部分,客户端应继续发送请求的其余部分。
- 101 Switching Protocols:表示服务器已接受客户端的请求,并将协议切换到客户端请求的协议。
2xx 成功状态码
这些状态码表示请求已成功处理。
- 200 OK:请求成功,通常与
GET
或POST
请求一起使用。 - 201 Created:请求成功并且服务器创建了一个新的资源,通常与
POST
请求一起使用。 - 204 No Content:请求成功,但没有返回任何内容,通常用于
DELETE
请求。
3xx 重定向状态码
这些状态码表示请求需要进一步操作才能完成。
- 301 Moved Permanently:请求的资源已被永久移动到新位置,响应包含
Location
头部指向新位置。 - 302 Found:请求的资源临时移动到新位置,响应包含
Location
头部指向新位置。 - 304 Not Modified:资源未被修改,客户端可以使用缓存的版本。
4xx 客户端错误状态码
这些状态码表示客户端请求存在问题。
- 400 Bad Request:服务器无法理解请求,通常由于语法错误。
- 401 Unauthorized:请求未经授权,需要进行身份验证。
- 403 Forbidden:服务器拒绝请求,客户端无权访问请求的资源。
- 404 Not Found:请求的资源未找到。
- 405 Method Not Allowed:请求的方法被禁止,通常表示客户端请求使用了不被允许的 HTTP 方法。
5xx 服务器错误状态码
这些状态码表示服务器在处理请求时发生错误。
- 500 Internal Server Error:服务器遇到意外情况,无法完成请求。
- 501 Not Implemented:服务器不支持请求的方法或功能。
- 502 Bad Gateway:服务器作为网关或代理时,从上游服务器收到无效响应。
- 503 Service Unavailable:服务器当前无法处理请求,通常由于过载或维护。
- 504 Gateway Timeout:服务器作为网关或代理时,未能在规定时间内从上游服务器收到响应。
let
const
和 var
for...in
和 for...of
的区别
对于数组:for...in
遍历的是数组的索引,for...of
遍历的是数组的值。
const arr = ['jiang', 'jia', 'qi', '!'];
for (let i in arr) {
console.log(i); // 0 1 2 3
}
for (let i of arr) {
console.log(i); // jiang jia qi !
}
对于对象:for...in
遍历的是对象的键,for...of
无法直接遍历对象,但是可以通过 Object.values(obj)
遍历对象的值。
const obj = {
firstName: 'jiaqi',
lastName: 'jiang',
age: 25,
};
for (let i in obj) {
console.log(i); // firstName lastName age
}
for (let i of Object.values(obj)) {
console.log(i); // jiaqi jiang 25
}
这里需要注意的是:for (let key of Object.keys(data))
迭代的是 data
对象的所有键,以数组的形式返回,适用于需要处理键的数组。而 for (let key in data)
则直接遍历对象的可枚举属性,可以包含原型链上的属性。通常推荐使用 Object.keys()
以确保只访问对象自身的属性,避免潜在的错误。
Vue2 和 Vue3 的区别
- 响应式的实现方式。
- vue2使用选项式API,在vue3中采用组合式API,将同一个功能的代码集中起来处理,使得代码更加有序,有利于代码的书写和维护。
关于 express 中间件
在 Express 中,中间件是处理请求和响应过程中的一个重要概念。简单来说,中间件是一个函数,它接收请求对象 (req
)、响应对象 (res
) 和一个 next
函数,用于控制请求的流转。
中间件的功能
请求处理:中间件可以用于解析请求体、添加头信息、验证用户身份等。
路由控制:中间件可以根据请求的路径和方法决定接下来的操作。
错误处理:中间件可以捕获并处理错误。
响应控制:可以在中间件中结束响应或将控制权传递给下一个中间件。
中间件的使用示例
// 自定义中间件
const myMiddleware = (req, res, next) => {
console.log(`请求路径: ${req.path}`);
next(); // 调用下一个中间件
};
// 应用中间件
app.use(myMiddleware);
// 处理请求的路由
app.get('/example', (req, res) => {
res.send('这是一个示例路由');
});
在这个例子中,myMiddleware
会在每次请求到达 /example
路由之前被调用,打印请求路径。调用 next()
函数后,请求会继续传递到下一个中间件或路由处理器。
中间件的种类
内置中间件:如
express.json()
和express.urlencoded()
,用于解析请求体。第三方中间件:如
morgan
(用于日志记录)、cors
(用于跨域资源共享)等。自定义中间件:可以根据需要创建自己的中间件。
通过中间件,Express 提供了非常灵活和强大的请求处理机制,使得构建 web 应用变得更加简单和可维护。
websocket 基本使用
在客户端(例如在浏览器中),你可以通过 WebSocket
构造函数建立连接:
const socket = new WebSocket('ws://localhost:8080');
// 连接成功后触发
socket.onopen = () => {
console.log('连接已建立');
// 发送消息到服务器
socket.send('你好,服务器!');
};
// 接收到消息时触发
socket.onmessage = (event) => {
console.log(`收到消息: ${event.data}`);
};
// 处理错误
socket.onerror = (error) => {
console.error('WebSocket 发生错误:', error);
};
// 连接关闭时触发
socket.onclose = () => {
console.log('连接已关闭');
};
跨域相关
预检请求(Preflight Request)是 CORS(跨域资源共享)机制中的一部分。它是浏览器在发送跨域 POST、PUT、DELETE 等请求之前,先发送一个 HTTP OPTIONS 请求,以检查目标服务器是否允许该跨域请求。
预检请求的工作原理
触发条件:当发起的跨域请求使用了非简单方法(如 POST、PUT、DELETE)或包含自定义请求头时,浏览器会自动发送预检请求。
OPTIONS 请求:预检请求使用 HTTP OPTIONS 方法,浏览器会请求服务器返回支持的请求方法和头部。
服务器响应:服务器需要在响应中包含适当的 CORS 头部,如
Access-Control-Allow-Origin
、Access-Control-Allow-Methods
和Access-Control-Allow-Headers
。如果服务器允许该请求,浏览器才会继续发送实际的请求。
flex: 0 1 auto
在 Flexbox 中,flex: 0 1 auto
是一个简写属性,用于控制 flex 项的大小和伸缩行为。它包含三个值:
flex-grow
(0):表示该项不会放大以填充额外空间。换句话说,如果有多余的空间,设置为 0 意味着这个项不会占用它。flex-shrink
(1):表示该项可以缩小以适应容器的大小。当空间不足时,这个项会根据比例缩小。flex-basis
(auto):表示该项的初始大小。设置为auto
意味着该项的大小由其内容决定,或者使用 CSS 设置的宽度/高度。
总结:
flex: 0 1 auto
表示该项不会放大,但可以缩小,其初始大小由内容决定。这个组合通常用于在灵活布局中保持项的基本尺寸,同时允许它在空间不足时缩小。
flex: 1
和 flex: auto
是 CSS Flexbox 中的两个属性值,虽然它们在某些方面相似,但具体的表现和含义不同。
flex: 1
- 等效于:
flex-grow: 1; flex-shrink: 1; flex-basis: 0%
- 行为:
- 允许元素在主轴方向上扩展以占用可用空间。
- 允许元素缩小以适应容器。
- 初始大小(
flex-basis
)为 0,这意味着元素在分配空间时不会占用任何空间,所有的可用空间都由flex-grow
分配。
- 用法: 通常用于想要让多个子元素平分可用空间的情况。
flex: auto
- 等效于:
flex-grow: 1; flex-shrink: 1; flex-basis: auto
- 行为:
- 允许元素在主轴方向上扩展以占用可用空间。
- 允许元素缩小以适应容器。
- 初始大小(
flex-basis
)为auto
,这意味着元素的大小将根据其内容决定。
- 用法: 适用于希望元素根据其内容进行调整,同时仍然能够扩展以填充可用空间的情况。
总结
flex: 1
:元素的初始大小为 0,所有可用空间均分。flex: auto
:元素的初始大小根据内容确定,并在需要时扩展以填充可用空间。
根据布局需求的不同,可以选择适合的 flex
属性值。
浏览器发送请求的 API
- XMLHttpRequest:这是传统的方式,用于发送 HTTP 请求,支持同步和异步请求。
- Fetch API:现代的 API,基于 Promise,提供更简洁的请求方式。
- Axios:一个基于 Promise 的库,简化 HTTP 请求。
diff 算法思路
Diff 算法主要用于比较两个数据结构(如树、虚拟 DOM)之间的差异,以便高效地更新界面。以下是 Diff 算法的一般过程:
- 树的比较
- 节点类型:比较两个节点的类型,如果类型不同,则直接替换整个节点。
- 节点标识:如果是同一类型的节点,使用唯一标识符(如
key
)来确认节点是否相同。
- 子节点的比较
- 深度优先遍历:递归地比较节点的子节点,检查它们的属性和内容差异。
- 顺序的处理:对于同层级的节点,Diff 算法会尝试通过最小化操作(如移动、添加、删除)来更新 DOM。
- 更新操作
- 插入和删除:根据比较结果,添加新节点或删除旧节点。
- 更新属性:修改节点的属性(如
class
、style
、事件监听等)。
- 优化策略
- 只比较相同层级的节点:通过使用
key
值来优化节点的查找,避免不必要的比较。 - 批量更新:收集所有的变更并一次性更新 DOM,减少重绘和重排的次数。
总结
Diff 算法的核心在于通过最小化比较和更新操作,提高性能,确保用户界面高效地响应数据变化。它是现代前端框架(如 React、Vue)实现高效渲染的基础。
css 隐藏元素
要隐藏一个元素,可以使用几种不同的 CSS 方法。以下是常见的几种:
- 使用
display: none;
.element {
display: none;
}
- 该方法完全从文档流中移除元素,不占据空间。
- 使用
visibility: hidden;
.element {
visibility: hidden;
}
- 该方法隐藏元素,但仍然保留其在文档流中的空间。
- 使用
opacity: 0;
.element {
opacity: 0;
}
- 该方法将元素变为完全透明,但仍然可以与用户交互。
js 判断数据类型的方法
在 JavaScript 中,可以通过多种方法判断数据类型。以下是几种常用的方法:
1. typeof
操作符
typeof
是最简单的方法,用于判断基本数据类型。
console.log(typeof 123); // "number"
console.log(typeof 'hello'); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null); // "object" (这是一个历史遗留问题)
console.log(typeof Symbol()); // "symbol"
console.log(typeof BigInt(123)); // "bigint"
2. Array.isArray()
用于判断是否为数组。
console.log(Array.isArray([1, 2, 3])); // true
console.log(Array.isArray({})); // false
3. instanceof
操作符
用于判断对象是否为某个构造函数的实例。
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date); // true