易混淆的一些基础知识点
px em rem vw vh
- px 是绝对单位
- em 是相对单位,元素 font-size 属性相对于父元素的 font-size;而元素的 width height 属性则相对于元素本身的 font-size。举例说明
<div style="font-size: 12px">
<p>追公交车的小仙女</p>
<p style="font-size: 2em;">追公交车的小仙女</p>
<p style="font-size: 16px;width: 20em;border:1px solid black;">追公交车的小仙女</p>
</div>
第一个 p 标签字体继承父元素为 12px;
第二个 p 标签字体相对于最近的父元素为:212px = 24px;
第三个 p 标签 width 相对于自身的 font-size, 宽度为 2016 = 320px.

- rem 则是相对于 HTML 根元素 font-size 设置;多数浏览器默认 HTML 字体大小为 16px,则 1rem = 16px
- vw 和 vh 也是相对单位,分别表示浏览器视口的宽度和高度。100vw 表示 100% 的浏览器视口宽度,50vw 则表示 50% 的浏览器视口宽度。PS:当浏览器放大缩小时,100vw 和 100vh 也随着视口大小变化而变化。

- vmin 和 vmax。10vmin 表示 10vw 和 10vh 较小的那个;10vmax 则表示 10vw 和 10vh 较大的那个
| 标题 | |
|---|---|
fetch XMLHttpRequest
fetch和xhr都是用于实现Ajax的技术方案。fetch被提出来是为了取代xhr的使用,因为xhr的定义比较繁琐,而fetch的语法更简洁,并返回一个 Promise 对象。- 对于 4XX 或者 5XX 的返回码,它们不会导致
fetch的 Promise 变为 rejected,但是返回的响应对象会具有ok属性,当ok为 false 时表示请求失败。只有在网络故障或请求被取消时,Promise 的状态才会被标记为 rejected。 fetch无法获取文件上传的进度。fetch不支持设置请求超时。- 无论是
fetch还是xhr,跨域请求时都不会自动发送跨域 Cookie。
节流和防抖
节流:在指定时间内限制函数执行的次数,保证函数不会被频繁调用 应用场景:滚动事件、窗口大小调整。
function throttle(fn, delay){
let timer
return function(){
if(!timer){
timer = setTimeout(function(){
fn.apply(this, arguments)
timer = null
}, delay)
}
}
}
防抖是确保在一定延迟时间后再执行函数,如果在这段时间内再次触发函数,那么将重新计算延迟时间。 应用场景:搜索框输入时,用户停顿输入后再触发搜索事件。
function debounce(fn, delay){
let timer
return function(){
clearTimerout(timer)
timer = setTimeout(function(){
fn.apply(this, arguments)
}, delay)
}
}
for of 与 for in
for in 循环遍历可枚举的数据,得到 key,如对象。
for of 循环遍历可迭代的数据,得到 value,如数组、Map、Set。
其中数组、字符串、类似数组的对象(arguments、NodeList) 既可以使用 for in 来遍历得到 key(index);也可以使用 for of 遍历得到值。
而对象不能用 for of 循环;Map 和 Set 则不可以用 for in 循环。
for of 是 ES6 的语法,数据只要具备可迭代性 (iterable) 便可使用 for of 循环,具有 Symbol.iterator 属性即可。通过实现 Symbol.iterator 属性,我们可以自定义对象的迭代行为。例如,一个简单的可迭代对象:
const myIterable = {
0: 'a',
1: 'b',
2: 'c',
[Symbol.iterator](){
let index = 0
return {
next: () => {
if (index<3) {
return { value: this[index++], done: false}
}
return {
value: undefined,
done: true
}
}
}
}
}
for(let v of myIterable){
console.log(v) // 输出 a, b, c
}
三次握手 vs 四次挥手
三次握手:建立连接
-
客户端向服务器发送一个 SYN 包,表示客户端希望建立连接
-
服务器收到 SYN 包后,向客户端发送一个 SYN/ACK 包作为回应,表示服务器接受了连接请求,并准备好通信
-
客户端收到服务器的 SYN/ACK 包后,发送一个 ACK 包给服务器,表示客户端确认连接已建立,可以开始通信

四次挥手:TCP 断开连接
-
客户端向服务器发送一个 FIN 包,表示客户端已经没有数据要发送了
-
服务器收到客户端的 FIN 包,向客户端发送一个 ACK 包,表示服务器已经接收到了客户端的 FIN 包
-
当服务器准备好关闭连接时,向客户端发送一个 FIN 包,表示服务器已经没有数据需要发送了
-
客户端收到服务器的 FIN 包后,向服务器发送一个 ACK 包,表示客户端已经接收到了服务器的 FIN 包。

强缓存和协议缓存
缓存策略:在第一次请求资源后,浏览器会收到包含 Cache-Control 和 Expires 的响应头,根据这些信息来判断是否使用强缓存。如果满足强缓存条件,则在第二次请求该资源时,浏览器会直接从本地缓存获取数据,无需发送请求到服务器,响应状态码为200。
如果不满足强缓存条件,浏览器会携带资源的标识(Last-Modified 或 ETag)通过请求头 If-Modified-Since 和 If-None-Match 向服务器发送请求,以判断是否使用协商缓存。如果服务器判断资源未更新,则返回响应状态码304,浏览器将继续使用本地缓存;如果资源已更新,服务器会返回新的资源及其响应头(包括 Cache-Control、Expires、Last-Modified 和 ETag 等),同时设置响应状态码为200,表明本次请求已获取新的资源。
总结一下,浏览器缓存可以分为两种:强缓存和协商缓存。
- 强缓存:浏览器直接使用本地缓存中的资源,不会向服务器发送请求,响应状态码为200。通过设置请求头的 Cache-Control 或 Expires(分别代表相对时间和绝对时间)来实现。
- 协商缓存:在强缓存失效后,浏览器会携带资源的标识(If-Modified-Since 或 If-None-Match)向服务器发送请求,询问资源是否有更新。如果资源未更新,响应状态码为304,浏览器继续使用本地缓存;如果资源已更新,则服务器会返回新的资源和响应头,并设置响应状态码为200。
| 返回码 | 是否发送请求 | ||
|---|---|---|---|
| 强缓存 | Cache-Control、expires | 200 ![]() | 否 |
| 协议缓存 | Last-Modified/If-Modified-Since、Etag/If-None-Match | 304 或者 200 ![]() | 是 |
F5 和 Control+F5
F5 刷新页面,浏览器会检查缓存是否过期,会带上 If-Modified-Since 和 If-None-Match 标识符。
control + F5 强制刷新页面,强制浏览器忽略缓存。Cache-Control 为 no-cache
跨域
跨域问题产生的原因是浏览器为了保护用户数据安全而实施的同源策略(Same-Origin Policy)。同源策略要求协议、域名、端口均相同,否则浏览器将限制请求。请求已经发送到服务器并得到响应,但由于同源策略的限制,浏览器无法处理这些响应数据。 以下是解决跨域问题的几种方法:
- 服务器设置 CORS:在服务器的响应头中加入 Access-Control-Allow-Origin 字段,设置允许访问的源地址。这样,浏览器会允许跨域请求成功。
- JSONP:JSONP 利用浏览器对 script 标签的跨域支持来实现。服务器返回一个包含回调函数的 JSON 数据,客户端解析并执行这个回调函数,从而获得响应数据。
- 代理服务器:由于浏览器的同源策略限制,前端不能直接请求跨域的资源。为了绕过这个限制,前端可以将请求发送到自己的服务器,然后由服务器去请求目标资源。这样,在前端看来,请求的目标仍然是自己的服务器,也就遵循了同源策略。当目标资源返回数据时,服务器将数据原样返回给前端,从而实现跨域请求。 nginx反向代理-解决前端跨域问题 - 一品码农 - 博客园
跨域带 cookie
- 服务端需要设置
Access-Control-Allow-Origin: [特定域名] // 不可以是 *
Access-Control-Allow-Credentials: true
- 客户端 XMLHttpRequest 发请求需要设置 withCredentials=true; fetch 发请求需要设置 credentials = include
nth-child
- :nth-child(3) 数字3,表示第3个子元素
- :nth-child(n+3) 变量 n+3 表示从第3个子元素开始,包括第3、4、5、6等
- :nth-child(5n) 变量 5n,表示第5、10、15个子元素
- :nth-child(2n+1) 或者 :nth-child(odd) 表示第1、3、5奇数个子元素
- :nth-child(2n) 或者 :nth-child(even)表示第2、4、6偶数个子元素
- :nth-child(-n+2) 表示前2两个元素
nth-last-child
- :nth-last-child(2) 数字2,表示倒数第2个子元素
- :nth-last-child(-n+2) 变量 -n+2 表示最后2个元素
- :nth-last-child(3n+4) 表示倒数第4、7、10、13等元素
nth-of-type nth-last-of-type
<div>
<div>这段不参与计数。</div>
<p>这是第一段。</p>
<p class="fancy">这是第二段。</p>
<div>这段不参与计数。</div>
<p class="fancy">这是第三段。</p>
<p>这是第四段。</p>
</div>
/* 奇数段 */
p:nth-of-type(2n+1) {
color: red;
}
/* 偶数段 */
p:nth-of-type(2n) {
color: blue;
}
/* 第一段 */
p:nth-of-type(1) {
font-weight: bold;
}
/* 这将匹配第三个段落,因为它匹配的元素是 2n+1 并且具有 fancy 类。
第二个段落具有 fancy 类,但不匹配,因为它不是:nth-of-type(2n+1)。
*/
p.fancy:nth-of-type(2n + 1) {
text-decoration: underline;
}

可以看出来 nth-of-type 的计数方式是只包括某个类型,而不是全部的元素。nth-child 则是计算所有类型的子元素。
box-sizing
clientHeight offsetHeight scrollHeight
clientHeight: 元素内部可见部分的高度,包括padding但不包括border、margin和水平滚动条。offsetHeight: 元素的布局高度,包括padding、border和水平滚动条(如果存在)。scrollHeight: 元素内容的总高度,包括可见部分和溢出的不可见部分,包括padding但不包括border、margin和水平滚动条。
<style>
#app{
height: 200px;
width: 300px;
box-sizing: border-box;
padding: 10px;
border:20px solid black;
overflow: auto;
}
#inner{
height: 500px;
background-color: red;
}
</style>
<div id="app">
<div id="inner"></div>
</div>
针对 ID 为 'app' 的容器,我们有以下计算结果:
-
clientHeight:- 计算:200px (总高度) - 20px (上边框) - 20px (下边框)
- 结果:160px (不包括 border 和 margin)
-
offsetHeight:- 计算:200px (总高度, 包括 border、padding 和可见内容区域)
- 结果:200px (包括 border)
-
scrollHeight:- 计算:500px (文本内容高度) + 10px (上内边距) + 10px (下内边距)
- 结果:520px (包括 padding)
当滚动条滑动到最下面的时候,有一个恒等式如下:
element.scrollHeight === element.scrollTop + element.clientHeight
因为 scrollHeight 表示内容的总高度,包括可见部分和溢出的不可见部分;clientHeight 表示元素内部可见部分的高度,当滚动条滑动到最下面的时候,scrollTop 表示不可见部分的高度,所以有上面的恒等式。
数组操作
JavaScript 中数组方法具有多种功能和用途,以下为对提到的方法的归纳:
-
返回新的数组:
map(fn): 对数组中的每个元素应用函数 fn,将结果组成新的数组返回。filter(fn): 根据函数 fn 的条件筛选数组中的元素,将符合条件的元组成新的数组返回。concat(arr): 将给定的数组 arr 拼接到当前数组末尾,生成一个新数组返回。
-
返回布尔值:
some(fn): 判断数组中是否存在至少一个元素满足函数 fn 的条件,若存在,返回 true,否则返回 false。every(fn): 判断数组中的所有元素是否都满足函数 fn 的条件,若都满足,返回 true,否则返回 false。
-
返回具体元素:
find(fn): 返回数组中第一个满足函数 fn 条件的元素,若不存在,返回 undefined。
-
返回 index (索引):
findIndex(fn): 返回数组中第一个满足函数 fn 条件元素的索引,若不存在,返回-1。indexOf(element): 返回数组中第一个与 element 相等的元素的索引,若不存在,返回-1。
-
Array 操作:
Array.from(obj): 将类数组对象或可迭代对象转换为新数组。Array.isArray(obj): 判断给定对象 obj 是否为数组,若是则返回 true,否则返回 false。Array.fill(value[, start[, end]]): 使用值value填充数组的一部分(从start索引开始到end索引结束,不包括end索引),若start和end省略,默认填充整个数组。
-
遍历:
forEach(fn): 对数组中的每个元素执行函数 fn,无返回值。for ...of: 语句用于遍历数组或可迭代对象的元素。reduce(fn[, initialValue]): 对数组中的每个元素执行函数 fn,并将结果累计到一个单一的返回值,可设置初始值 initialValue。
-
操作原数组:
push(element): 向数组末尾添加一个或多个元素,并返回新的数组长度。pop(): 删除数组最后一个元素,并返回被删除的元素。unshift(element): 向数组开头添加一个或多个元素,并返回新的数组长度。shift(): 删除数组第一个元素,并返回被删除的元素。reverse(): 反转数组的顺序,返回反转后的原数组。
-
截取数组:
slice(start[, end]): 提取原数组的一部分(从start索引开始,不包括end索引),返回新数组。splice(start[, deleteCount[, item1[, item2[, ...]]]]): 在原数组上删除、替换或添加元素,返回一个包含被删除元素的数组。
-
排序:
sort([compareFunction]): 对数组的元素进行排序,返回排序后的原数组。
根据所需功能,可以灵活选择和使用这些数组方法来实现各种需求。
垃圾回收机制
标记-清除:这个算法主要分为两个阶段:(1)标记(mark):垃圾回收器从根对象(如全局对象 window)开始,递归遍历所有可访问的对象,并标记这些对象为”活动“的。一个活动对象指的是该对象在程序中可能仍然会访问和使用。(2)清除(sweep):在所有活动对象被标记后,将遍历所有分配的内存空间,并清除未被标记为活动的对象所占据的内存。这意味着没有其他对象引用这些对象,他们变得不可访问,因此他们的内存空间可以被安全回收。
以下是一个简单的示例,说明了标记-清除算法的工作原理:
function createObject() {
const a = {"key": {"nestedKey": "value"}};
const b = {"key": a.key};
a.key = null;
}
createObject();
在这个例子中,当调用 createObject() 函数时,它创建了两个对象 a 和 b。对象 a 的 key 属性包含一个具有 nestedKey 属性的嵌套对象,b 的 key 属性引用了相同的嵌套对象。在函数运行结束时,局部变量 a 和 b 都超出了它们的作用域, 变得不可访问,并且对象 a 的 key 属性被设置为 null。
此时标记-清除算法会执行以下操作:
标记:从全局对象开始,检查所有可访问的活动对象。在这种情况下,不存在对局部变量 a 和 b 或其包含的任何对象的引用。因此,它们将不会被标记为“活动”。
清除:在标记阶段之后,垃圾回收器将清除所有未被标记为“活动”的内存空间。因此,局部变量 a 和 b 以及嵌套对象的内存将被释放。
引用计数(Reference Counting): 引用计数是一种早期的垃圾回收策略。在这种策略中,对象的引用(或指针)被计数。初始计数值为1,在每次对象被引用时,计数值递增;在对象被释放时,计数值递减。 当对象的引用计数值变为0时,即代表没有对该对象的引用,此时将对象视为垃圾,收回其占用的内存。尽管引用计数可以实现垃圾回收,但它有一个缺陷,即会导致循环引用的内存泄漏。循环引用出现在两个或多个互相引用的对象上,而这些对象实际上在程序中是无用的。即使这些对象的计数不为0,它们仍然应该被视为垃圾。由于引用计数无法检测到这种情况,这会导致内存泄漏。
NodeList vs HTMLCollection

如图所示,Node 类型是 Element 类型的父类,而 Element 类型又是 HTMLElement 的父类。
NodeList: NodeList是一个表示DOM节点列表的集合类型。这个列表可以是动态的(实时更新)或静态的
(不受文档改变影响)。例如,通过使用querySelectorAll方法获取的DOM NodeList是静态的:
const nodes = document.querySelectorAll('.example-class');
通过childNodes属性获取的NodeList是动态的(实时更新):
const nodes = document.getElementById('parent').childNodes;
HTMLCollection: HTMLCollection是一个简单的表示HTML元素列表的集合类型。这个列表是实时更新的。例如,通过使用getElementsByClassName方法获取的DOM HTMLCollection:
const elements = document.getElementsByClassName('example-class');
在语法和用法上,NodeList和HTMLCollection相似,但有一些关键区别:
- NodeList可以包含任何类型的DOM节点,而HTMLCollection仅包含HTML元素节点。
- NodeList可以是静态的或实时更新的,而HTMLCollection总是实时更新的。
NodeList.prototype.forEach()方法可以直接在NodeList上使用,但对HTMLCollection而言需要先将其转换为数组才能使用forEach()方法。
querySelectorAll vs getElementsByClassName
1. querySelectorAll():
querySelectorAll() 方法接受一个 CSS 选择器字符串作为参数,并返回一个包含匹配选择器的所有元素的静态 NodeList。这意味着获取到的列表不会随着 DOM 变化而更新。
示例:
const elements = document.querySelectorAll('.example-class');
getElementsByClassName():
getElementsByClassName() 方法接受一个类名字符串作为参数,并返回一个包含具有该类名的所有元素的实时更新的 HTMLCollection。当 DOM 中的元素发生变化(例如添加或删除了元素)时,返回的 HTMLCollection 会实时更新。
示例:
const elements = document.getElementsByClassName('example-class');
querySelectorAll()接受任意 CSS 选择器作为参数,而getElementsByClassName()仅接受类名作为参数。querySelectorAll()返回一个静态的NodeList,当 DOM 发生变化时,集合不会更新。而getElementsByClassName()返回一个实时更新的HTMLCollection。querySelectorAll()返回的集合包含任何类型的节点,而getElementsByClassName()仅包含 HTML 元素节点。querySelectorAll()的返回值(NodeList)具有forEach方法,可以直接遍历集合。要在getElementsByClassName()的返回值(HTMLCollection)上使用forEach方法,需要将其转换为数组。
var let const
var 不在函数内声明的变量,在全局声明的变量会绑定到 window 上。有定义提升的特点,即在定义前方位变量,为 undefined,不会报错。 let const 没有定义提升的特点,不能在定义前访问,const 声明的时候需要赋值。
- 尽量不要使用 var
- 优先使用 const, let 次之。
margin 重叠
相邻元素重叠
one 和 two 元素 margin 会取最大值 30px。
<style>
.one{
margin-bottom: 20px;
height: 100px;
}
.two{
margin-top:30px;
height: 100px;
}
</style>
<body>
<div class="one"></div>
<div class="two"></div>
</body>
解决:在 two 外面包一层为 BFC 的div。BFC 的 div 和子元素 margin 不会重叠。从而使得 one 和 two 可以正常按照 20 + 30 = 50px 隔离。这种方式,其实并没有消除相邻元素 margin 重叠的问题,只是不让有 margin 的元素相邻而已。
父子元素重叠
父元素 f 直接包含子元素 s,导致父元素上边距为 20px。解决办法:让父子元素”隔开“。父元素设置 padding\border。或者让父元素成为 BFC。
<style>
.f{
height: 300px;
width: 200px;
/* border: 1px solid black; */
background-color: gray;
margin-top: 10px;
}
.s{
height: 100px;
width: 100px;
/* border: 1px solid red; */
background: red;
margin-top: 20px;
}
</style>
<body>
<div class="f">
<div class="s"></div>
</div>
</body>
BFC
触发条件:
- 根元素 html
- 浮动元素 float 为 left/ right
- 绝对定位元素 position absolute/fixed
- overflow 不是 visible
- display inline-block table-cell table-caption table flex grid
特点:
清除浮动
为什么要清除浮动?父元素会出现高度塌陷的问题 清除浮动的原理:一种是真正意义的清除浮动 clearfix,另一种是父元素创建 BFC
闭包
flex
原型链

class Cat{
constructor(name){
this.name = name
}
}
const c = new Cat()
console.log(c.__proto__ === Cat.prototype)
console.log(Cat.prototype.__proto__ === Object.prototype)
console.log(Object.prototype.__proto__ === null)
实例 c 通过 __proto__ 或者 Object.getPrototypeOf(c) 可以拿到原型对象 Cat.prototype
c.__proto__ === Cat.prototype
Object.getPrototypeOf(c) === Cat.prototype
同时,Cat.prototype 也可以看成是一个 Object 的实例
Cat.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ 指向 null
-
实例对象有 proto 属性
-
构造函数(对象) 有 prototype 属性
-
原型对象 Cat.prototype 上的方法,会被实例天然继承
-
instanceof 右边必须是一个构造函数有 prototype 属性的对象
defer async
1. async:
当<script>标签具有async属性时,浏览器将异步加载JavaScript文件。这意味着脚本文件的加载不会阻塞HTML页面的渲染。然而,一旦脚本文件加载完成,浏览器会立即执行该脚本,这可能导致HTML解析过程中断。如果有多个async脚本,它们将按照加载顺序执行,而非它们在HTML文档中出现的顺序。完全独立,跟 DOMContentLoaded 无关。
2.defer:
当<script>标签具有defer属性时,浏览器将延迟加载JavaScript文件,直到HTML文档完全解析完毕。这意味着脚本文件的加载和执行不会阻塞HTML页面的渲染。与async不同,defer脚本会按照它们在HTML文档中出现的顺序执行。DOMContentLoaded 之前执行。
区别:
async属性会立即执行加载完成的脚本(可能在HTML解析过程中),而defer属性会在HTML解析完成后按脚本出现的顺序执行。async属性不保证脚本按HTML文档中的顺序执行,而defer属性保证按顺序执行。- 两者都使脚本的加载与HTML解析并行,避免阻塞渲染。

DOMContentLoaded load
DOMContentLoaded 事件:
DOMContentLoaded事件在浏览器完成对HTML文档的解析(即构建完DOM树)后触发,但在此之前不会等待图片、样式表、iframe等外部资源的加载。这使得DOMContentLoaded事件更早地触发,通常用于在DOM就绪时设置交互和初始化组件。
document.addEventListener("DOMContentLoaded", function () {
console.log("DOM content is loaded and parsed.");
// 初始化组件或设置交互
});
load 事件:
load事件在浏览器完成对整个页面的加载(包括外部资源,如:图片、样式表、脚本、iframe等)后触发。在处理依赖于外部资源(尤其是图片和其他媒体文件)的操作时,通常使用load事件。
window.addEventListener("load", function () {
console.log("The entire page and all its resources are loaded.");
// 处理依赖于外部资源的操作
});
Http
HTTP 1.0
- 最基础的 HTTP 协议
- 支持基本的 GET POST 方法
HTTP 1.1
- 缓存策略 cache-control e-tag
- 支持长连接 Connection: keep-alive,一次 TCP 连接多次请求
- 断点续传,状态码 206
- 支持新的方法 PUT DELETE 等,可用于 Restful API
HTTP 2.0
- 可压缩 header, 减少体积
- 多路复用,一次 TCP 连接中可以多个 HTTP 并行请求
- 服务端推送
preload vs prefetch
- preload 资源在当前页面使用,会优先加载
- prefetch 资源在未来页面使用,空闲时加载
<head>
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="main.js" as="script">
<link rel="prefetch" href="other.js" as="script">
</head>
<body>
<!-- 使用 -->
<script src="main.js" defer></script>
</body>
window 和 document 事件
window 对象表示浏览器窗口,它是 JavaScript 的顶级对象,所有其他对象如 Array、Date 等都是这个对象的子对象。Window 对象包括了一些全局函数,例如 alert() confirm() 等,另外还有一些全局属性,例如 document、location 等。window 对象还包括了一些与窗口交互的功能,例如打开新的浏览器窗口 window.open(),改变当前窗口的位置和大小等等。
document 对象,表示的是 HTML 文档,是 window 对象的一个属性。document对象是Web页面在浏览器中的编程接口。它提供了对网页内容的全面访问和操作。例如,我们可以用document对象的函数(比如getElementById())来获取和操作HTML元素。
window 通常可以监听以下事件:
- load
- unload
- beforeunload
- resize
- scroll
- error
- popstate
对于 document 对象可以监听:
- click
- dbclick
- mousedown
- mouseup
- DOMContentLoaded
转载自:https://juejin.cn/post/7247050634192355386

