JavaScript下

this指向

this的绑定规则

this的指向

  • 在函数调用的时候,JavaScript会 默认给this绑定一个值
  • this的 绑定和定义的位置(即编写的位置) 没有关系
  • this的 绑定和调用方式以及调用的位置有关系
  • this 是在运行时才被绑定的

默认绑定

  • 独立的调用函数this指向window,但在严格模式下独立调用的函数中的this指向的是Undefined(“use strict”)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>

<script>

// 默认绑定
// 1.案例一
function foo() {
console.log(this)
}

foo()

// 2.案例二
function test1() {
console.log(this)
test2()
}

function test2() {
console.log(this)
test3()
}

function test3() {
console.log(this)
}
test1()

// 3.案例三
function project(func) {
func()
}

var obj = {
name: "jojo",
bar: function() {
console.log(this)
}
}

project(obj.bar)

</script>

</body>
</html>

隐式绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>

function foo() {
console.log(this) //这里的this绑定到obj对象
}

var obj = {
bar: foo
}

obj.bar()

</script>
</body>
</html>

new绑定

  • 执行的操作
    • 创建一个全新的对象
    • 这个新对象会被执行prototype连接
    • 这个新对象会绑定到函数调用的this上
    • 如果函数没有返回其他对象,表达式会返回这个新对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>

<script>

function foo() {
console.log(this);
this.name = "why"
}

new foo() //this绑定foo

</script>

</body>
</html>

显式绑定

call:func.call(thisArg, arg1, arg2, ...)
apply:func.apply(thisArg, [arg1, arg2, ...]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>

var obj = {
name: "why"
}

function foo(name, age) {
console.log(this)
console.log(name, age)
}

foo.call(obj, "jojo", 20) //执行函数,并且强制this指向obj
foo.apply(obj, ["小羊", 19])


</script>
</body>
</html>

bind

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function foo(name, age) {
console.log(this);
console.log(name);
console.log(age);
}

var obj = {
name: 'Tom'
}

var bar = foo.bind(obj, "jojo", 20)
bar()

</script>
</body>
</html>

绑定优先级

显式 > 隐式
new > 隐式
new > bind

this面试题

![[Pasted image 20240727141059.png]]

![[Pasted image 20240727191000.png]]

![[Pasted image 20240727211810.png]]

![[Pasted image 20240727211824.png]]

![[Pasted image 20240727220053.png]]

手写call、apply、bind函数

day36

箭头函数

  • 箭头函数 不会绑定this、arguments属性, 没有显示原型,不能和new一起使用
  • nums.forEach((item, index, arr) => { })
  • 优化:
    • 如果只有一个参数,( )可以省略![[Pasted image 20240725150801.png]]
    • 如果执行体只有一行代码,可以省略大括号,并且会返回这行代码的返回值![[Pasted image 20240725150853.png]]
    • 如果执行体只返回一个对象,需要给对象加上( )![[Pasted image 20240725150934.png]]

深入浏览器的渲染原理

网页被解析的过程

![[Pasted image 20240801121708.png]]

浏览器的内核

  • 常见的浏览器内核![[Pasted image 20240801123558.png]]
  • 浏览器内核也称作浏览器排版引擎

渲染页面的详细流程

![[Pasted image 20240801123756.png]]
解析HTML生成 DOM Tree ,遇到css文件时,解析CSS生成 Style Rules,这两者的解析过程不产生冲突。DOM Tree 和 Style Rules生成 Render Tree(渲染树,渲染树中没有节点的位置),再通过 Layout进行布局,然后进行绘画和展示

HTML解析过程

  • 解析HTML是所有步骤的开始(服务器给浏览器默认返回 .html 文件)
  • 解析HTML构建 DOM Tree![[Pasted image 20240801124525.png]]

生成CSS规则

  • 解析过程中,遇到CSS的 link元素,浏览器会下载对应的CSS文件(下载CSS文件不会影响DOM解析)
  • 下载完CSS文件,会对CSS文件进行解析,得到对应的 Style Rules(规则树, 也可以称之为CSSOM,CSS对象模型)![[Pasted image 20240801124907.png]]

构建Render Tree

  • DOM Tree 和 CSSOM Tree 结合构建 Render Tree![[Pasted image 20240801125031.png]]
  • link元素不会阻碍DOM Tree 的构建过程,但会阻碍 Render Tree 的构建过程,因为在构建Render Tree时,需要对应的CSSOM Tree
  • Render Tree 和 DOM Tree 并不是一一对应的关系, display为none的元素,不会出现在Render Tree中

布局(Layout)和绘制(paint)

  • 在Render Tree上运行 布局 来计算每个节点的几何体
    • Render Tree会显示节点及其他样式,但不显示 每个节点的尺寸、位置等信息
    • 布局可以确定呈现树中 所有节点的宽度、高度和位置信息
  • 将每个节点绘制到屏幕上
    • 绘制时,浏览器将布局的每个frame转为屏幕上实际的像素点
    • 包括将元素的可见部分进行绘制,如 文本、颜色、边框、阴影、替换元素(img等)

回流和重绘

  • 回流
    • 在第一次确定节点的大小和位置,称之为布局
    • 之后再进行修改重新计算称为回流
  • 引起回流的情况![[Pasted image 20240801131023.png]]
  • 重绘
    • 第一次渲染内容称之为绘制
    • 之后重新渲染称之为重绘
  • 引起重绘的情况
    • 修改背景色、文字颜色、边框颜色、样式等
      ![[Pasted image 20240801131232.png]]

特殊解析 - composite合成

  • 每个合成层都是 单独渲染的
  • 默认情况下,标准流中的内容都是被绘制在同一个图层(Layer)中
  • 有一些属性绘创建一个新的合成层,利用GPU来加速绘制
    • **3D transforms
    • **video、canvas、iframe
    • **opacity动画转换时
    • **position: fixed(固定定位)
    • will-change(目前还是一个实验性的属性,提前告诉浏览器元素可能发生的变化)
    • **animation 或 transition 设置了 opacity(透明度)、transform
  • 分层是以内存管理为代价提高性能,不能作为性能优化策略过度使用

script元素和页面解析的关系

  • 浏览器在解析HTML过程中,遇到了 script元素是不能继续构建DOM树的,会停止构建,先下载JavaScript代码,并且执行JavaScript的脚本,等到JavaScript脚本执行结束后,再继续解析HTML,构建DOM树
    ![[Pasted image 20240801142428.png]]

defer属性

  • defer属性告诉浏览器 不要等待脚本下载, 继续解析HTML, 构建DOM Tree, 如果脚本提前下载好了,它会 等待DOM Tree构建完成,在DOMContentLoaded事件完成前先执行defer中的代码
  • 多个带defer的脚本会保持正确的顺序执行
  • defer可以提高网页的性能,推荐放在head元素中
  • 注意: defer只适用于外部脚本,对于script默认内容会被忽略

async属性

  • async让一个脚本完全独立
    • async脚本不会阻碍浏览器的解析(与defer类似)
    • async脚本不能保证顺序,它独立下载、独立运行,不会等待其他脚本
    • async不能保证在DOMContentLoaded之前或者之后执行
  • defer常用于需要在文档解析后操作DOM的JavaScript代码,并且对多个script文件有顺序要求
  • async通常用于独立的脚本,对其他脚本,甚至DOM没有依赖的

深入JavaScript的运行原理

V8引擎的执行原理

![[Pasted image 20240801154302.png]]

V8引擎的架构

![[Pasted image 20240801154530.png]]

JS执行上下文

执行上下文

  • JS引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),用于执行代码的调用栈
  • 全局的代码块为了执行会构建一个Global Execution Context(GEC),GEC会被放入ECS中执行

认识VO对象

  • 每一个执行上下文会关联一个 VO(Variable Object,变量对象),变量和函数声明会被添加到这个VO对象中
  • 当全局代码被执行的时候,VO就是GO对象了

函数执行上下文

![[Pasted image 20240801172129.png]]

JavaScript的内存管理和闭包

JavaScript内存管理

  • JavaScript会在 定义数据时为我们分配内存
  • JavaScript对于 原始数据类型内存的分配 会在执行时,直接在栈空间进行分配
  • 对于 复杂数据类型内存的分配 会在堆内存中开辟一块空间,并且将这块空间的指针返回值变量引用

垃圾回收(GC)算法

引用计数

  • 一个对象有一个引用指向它 时,这个对象的的引用就 +1
  • 一个对象的引用为0 时,这个对象就可以被销毁掉
  • 弊端:会产生循环引用![[Pasted image 20240802152232.png]]

标记清除

  • 核心思路: 可达性
  • 实现思路: 设置一个 根对象,垃圾回收器会定期从这个根对象开始,找到所有从根开始有引用到的对象,对于没有引用到的对象,认为是不可用的对象
  • 这个算法解决了上一个算法产生的循环引用的问题![[Pasted image 20240802154857.png]]

算法优化

V8引擎为了进行更好的优化,在算法实现细节上会结合一些其他算法

  • 标记整理
    • 回收期间会将保存的储存对象 搬运汇集到连续的内存空间,从而 整合空闲空间,避免内存碎片化
  • 分代收集:对象被分为 新的旧的
    • 很多对象完成工作并很快死去,它们会被 很快被清理
    • 那些长期存活的对象会变 老旧,而且 被检查的频次也会减少
  • **增量收集
    • 将垃圾收集工作分成几个部分来做,然后将这几部分逐一处理,这样把一个大的延迟分成许多微小的延迟
  • **闲时收集
    • 垃圾收集器 只会在CPU空闲时尝试运行,减少可能对代码执行的影响

闭包

闭包的定义

  • 一个函数和周围的环境的引用捆绑在一起,这样的组合就是闭包
  • 闭包可以在一个内层函数中访问到其外层函数的作用域
  • 广义理解:JavaScript中的函数都是闭包
  • 狭义理解:JavaScript中的一个函数,如果访问了外层作用域的变量,那么它就是一个闭包

JavaScript函数的增强知识

函数对象的属性和argumens

属性name和length

  • 属性name:一个函数的名词我们可以通过name来访问![[Pasted image 20240802203605.png]]
  • 属性length:返回函数输入参数的个数(rest参数不参与参数的个数)![[Pasted image 20240802203941.png]]

认识arguments

  • arguments是一个类数组对象(不是一个数组类型,而是一个对象类型)
    • 它拥有数组的一些特性,如length、可以用Index索引来访问
    • 但没有数组的一些方法,如filter、map

arguments转Array

  • 方法一:遍历arguments,添加到一个新数组中![[Pasted image 20240802205908.png]]
  • 方法二:ES6中的两个方法
    • 1.Array.from![[Pasted image 20240802210001.png]]
    • 2.[...arguments]![[Pasted image 20240802210026.png]]
  • 方法三:调用slice函数的call方法![[Pasted image 20240802210124.png]]

函数的剩余(rest)参数

  • ES6中引用了rest parameter,可以将不定数量的参数放入到一个数组中
    • 最后一个参数是 … 为前缀,那么剩余的参数会作为一个数组放到该参数中![[Pasted image 20240803163950.png]]
  • 剩余参数和arguments的区别
    • 剩余参数只包含 没有对应形参的实参,arguments对象包含了 传给函数的所有实参
    • arguments对象不是一个数组,只是类数组对象,而rest参数是一个真正的数组,可以进行数组的所有操作
  • 剩余参数必须放到最后一个位置,否则会报错

纯函数

理解纯函数

  • 有确定的输入,一定会产生确定的输出
  • 函数在执行过程中,不能产生副作用

柯里化函数

![[Pasted image 20240803194335.png]]

自动柯里化(了解)

![[Pasted image 20240803201340.png]]

组合函数

![[Pasted image 20240803203237.png]]

严格模式

![[Pasted image 20240803210307.png]]

对象增强

Object.defineProperty

  • Object.defineProperty()方法会直接在对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象:Object.defineProperty(obj, prop, descriptor)
  • 可接受三个参数
    • obj:要定义属性的对象
    • prop:要定义或修改属性的名称或Symbol
    • descriptor:要定义或修改的属性描述符
  • 返回值被传递给函数的对象

数据属性描述符

![[Pasted image 20240805170950.png]]

存取属性描述符

![[Pasted image 20240805172134.png]]

同时定义多个属性

  • Object.defineProperties()方法直接在一个对象上定义多个新的属性或修改现有属性,并且返回该对象![[Pasted image 20240805173134.png]]

ES5中的继承

对象和函数的原型

认识对象原型

![[Pasted image 20240805192039.png]]

函数对象的原型

![[Pasted image 20240805192551.png]]
将方法放在原型上

  1. *减少内存占用:
    • 当方法定义在对象的实例上时,每个对象都会有一个独立的方法副本,这会浪费内存。
    • 将方法放在原型上,所有对象实例共享同一个方法,大大减少了内存的使用。
  2. 提高代码复用性
    • 将方法放在原型上,可以让所有对象实例都能访问和使用这些方法。
    • 这提高了代码的复用性,避免了在每个对象实例上都定义相同的方法,提高了开发效率。
  3. *动态添加/修改方法:
    • 通过修改原型,可以动态地为所有对象实例添加或修改方法。
    • 这使得代码更加灵活和可扩展。
  4. 保持对象实例的轻量级
    • 将方法放在原型上,可以保持对象实例本身更加简单和轻量级。
    • 对象实例中只保存自己的属性,方法都存储在原型上,这样可以提高性能。
  5. *继承和多态:
    • 通过原型链机制,可以实现继承和多态。
    • 子类可以重写或扩展从父类继承的方法,实现代码复用和多态特性。
      注意: 讲方法放在原型上的方法也叫做实例方法,在没有实例对象的情况下,不能调用此函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>

function Students(name, age, grade) {
this.name = name;
this.age = age;
this.grade = grade;
} // 定义构造函数

Students.prototype.running = function() {
console.log(this.name + " is running");
} // 将方法放在原型上

var std1 = new Students("jojo", 20, 100) // 创建实例对象

std1.running()

</script>
</body>
</html>

constructor属性

![[Pasted image 20240806141712.png]]
**理解
![[Pasted image 20240806145948.png]]

![[Pasted image 20240806145900.png]]

  • 重写原型对象![[Pasted image 20240806151052.png]]![[Pasted image 20240806151113.png]]![[Pasted image 20240806151123.png]]![[Pasted image 20240806151136.png]]

通过原型链实现继承

创建父类对象,并且作为子类的原型对象![[Pasted image 20240806160312.png]]![[Pasted image 20240806160344.png]]![[Pasted image 20240806160612.png]]

借用构造函数实现继承

![[Pasted image 20240806161649.png]]![[Pasted image 20240806161712.png]]![[Pasted image 20240806161725.png]]

  • 组合原型链和借用构造函数实现继承的问题![[Pasted image 20240806162633.png]]

寄生组合实现继承

![[Pasted image 20240806165456.png]]
最终实现方案:

  • 将继承函数封装成工具放在JS文件中![[Pasted image 20240806170337.png]]
  • 代码![[Pasted image 20240806170358.png]]

对象的方法补充

![[Pasted image 20240806173132.png]]

ES6实现继承

原型继承关系图

![[Pasted image 20240806211030.png]]

class方式定义类

  • 使用class来定义一个类:
    • 类声明和类表达式![[Pasted image 20240807133150.png]]
  • 在创建对象的时候想给类传递一些参数:
    • 每个类有一个固定的构造函数方法constructor
    • 每个类只能有一个构造函数
  • 注意:类中定义的多个内容不需要使用 “,” 进行分割

实例方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
constructor(name, age) {
this.name = name
this.age = age
}

//running和eating被添加在Person.prototype上
running() {
console.log(`${this.name} is running.`)
}

eating() {
console.log(`${this.name} is eating.`)
}
}

var p1 = new Person("jojo", 20)
p1.running()
p1.eating()

类的静态方法

  • 静态方法通常用于定义直接使用类来执行的方法,不需要有类的实例,使用 static关键字来定义
  • 类方法里面的this指向类本身
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>

class Person {
constructor(name, age) {
this.name = name
this.age = age
}

static running() {
console.log(`${this.name} is running.`);
}
}

Person.running() // 静态方法可以直接通过类名调用,不需要实例化对象,这里的this指向类本身,输出Person is running.

</script>

super关键字

  • 执行 super.method(…) 来调用一个父类方法
  • 执行 super(…) 来调用一个父类 constructor (只能在自己的constructor中调用)
  • 注意: 在子类的构造函数中使用this或者返回默认对象之前,必须先通过super调用父类的构造函数
  • super的使用位置有三个:子类的构造方法、实例方法、静态方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<script>

class Person {

constructor(name, age) {
this.name = name
this.age = age
}
running() {
console.log('running')
}
eating() {
console.log('eating')
}

static sleep() {
console.log('sleeping')
}
}

class Student extends Person {
constructor(name, age, grade) {
// 调用父类构造函数
super(name, age)
this.grade = grade
}

// 重写父类方法
running() {
console.log('running in class')
super.running() // 调用父类方法
}

// 新增方法
studying() {
console.log('studying')
}

// 重写父类静态方法
static sleep() {
console.log('sleeping in class')
super.sleep() // 调用父类静态方法
}
}

var stu = new Student("jojo", 18)
stu.running()
stu.eating()
stu.studying()
Student.sleep()

</script>

继承内置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<script>

// 1、创建一个新的类,继承内置类 Array
class xyArray extends Array {
get lastItem() {
return this[this.length - 1]
}

get firstItem() {
return this[0]
}
}

var arr = new xyArray(1, 2, 3, 4, 5)
console.log(arr) // [1, 2, 3, 4, 5]
console.log(arr.length) // 5
console.log(arr.lastItem) // 5
console.log(arr.firstItem) // 1

// 2、直接对Array进行扩展
Array.prototype.lastItem = function() {
return this[this.length - 1]
}

Array.prototype.firstItem = function() {
return this[0]
}

var arr2 = [1, 2, 3, 4, 5]
console.log(arr2) // [1, 2, 3, 4, 5]
console.log(arr2.length) // 5
console.log(arr2.lastItem()) // 5
console.log(arr2.firstItem()) // 1

</script>

类的混入mixins

  • JavaScript的类只支持单继承,当我们需要多继承时,可以使用混入![[Pasted image 20240807153120.png]]

ES6对象的增强

字面量的增强

  • 属性的简写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1、属性的增强
var name = "小明"
var age = 18

var obj = {
name, // 等同于 name: name
age // 等同于 age: age
}

function foo() {
var message = "Hello World"
var info = "This is a message from "
return {
message, // 等同于 message: message
info // 等同于 info: info
}
}

var result = foo()
console.log(result.message) // Hello World
console.log(result.info) // This is a message from
  • 方法的增强
1
2
3
4
5
6
7
8
9
10
// 2、方法的增强
var obj = {
name: "小明",
age: 18,
sayHello() { // 等同于 sayHello: function() {...}
console.log("Hello, " + this.name)
}
}

obj.sayHello() // Hello, 小明
  • 计算属性名
1
2
3
4
5
6
7
8
9
// 3、计算属性名
var key = "address"
var value = "北京市海淀区"
var obj = {
[key]: value // 等同于 obj[key] = value
}

console.log(obj.address) // 北京市海淀区

解构

  • 数组的解构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1.数组的解构语法
var arr = [1, 2, 3, undefined, 5]

// 1.1基本用法
var [a, b, c] = arr
console.log(a, b, c) // 1 2 3

// 1.2按照严格的顺序
var [a, , c] = arr
console.log(a, c) // 1 3

// 1.3解构出数组
var [a, b, ...arr2] = arr
console.log(a, b, arr2) // 1 2 [3, 4, 5]

// 1.4默认值
var [a , b, c, d = 4, e] = arr
console.log(a, b, c, d, e) // 1 2 3 4 5
  • 对象的解构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 2.对象的解构语法
var obj = {name: 'zhangsan', age: 20, city: 'beijing'}

// 2.1基本用法
var {name, age, city} = obj
console.log(name, age, city) // zhangsan 20 beijing

// 2.2对象的解构没有顺序,根据key值解构
var {age, name, city} = obj
console.log(name, age, city) // zhangsan 20 beijing

// 2.3解构出对象
var {name, age, ...obj2} = obj
console.log(name, age, obj2) // zhangsan 20 {city: "beijing"}

// 2.4默认值
var {name, age, city, job = 'teacher'} = obj
console.log(name, age, city, job) // zhangsan 20 beijing teacher

// 2.5对变量名进行重命名
var {name: myName, age: myAge, city: myCity} = obj
console.log(myName, myAge, myCity) // zhangsan 20 beijing

ES6新特性

let、const

基本使用

  • let关键字:与var没有太大的区别,都是用于声明一个变量
  • const关键字
    • 用const关键字声明的变量一旦被赋值,就不能被修改
    • 如果赋值的是引用类型,那么可以通过引用找到对应的对象,修改对象的内容![[Pasted image 20240810011845.png]]
  • let、const都不允许重复声明变量
  • let、const不会给window上添加任何属性

let、const的块级作用域

  • let、const、function、class声明的标识符具备块级作用域的限制![[Pasted image 20240810022228.png]]
  • 但是函数拥有块级作用域,但是在外面依然可以访问

应用场景

  • 获取多个按钮监听点击
    ![[Pasted image 20240810023404.png]]

模版字符串

  • ES6开始使用模版字符串来嵌入JS的变量或者表达式来进行拼接
    • 使用反引号来编写字符串,称为模版字符串
    • 再通过 ${expression} 来动态嵌入内容![[Pasted image 20240810025103.png]]

标签模版字符串

  • 使用标签模版字符串,在调用时插入其他变量
    • 模版字符串会被拆分
    • 第一个元素是数组,是被模版字符串拆分的字符串组合
    • 后面的元素是一个个模版字符串传入的内容![[Pasted image 20240810030121.png]]

展开运算符

展开语法

![[Pasted image 20240810032922.png]]
![[Pasted image 20240810033004.png]]

Symbol

![[Pasted image 20240810034748.png]]![[Pasted image 20240810034808.png]]
![[Pasted image 20240810035320.png]]![[Pasted image 20240810035344.png]]

Set-Map

Set

  • 基本使用
    • Set中的元素不能重复
    • 这个功能可以给数组去重![[Pasted image 20240810143447.png]]
  • 常见属性和方法
    • 属性
      • size:返回Set中元素的个数
    • 方法
      • add(value):添加某个元素,返回Set对象本身
      • delete(value):从Set中删除和value值相等的元素,返回Boolean类型
      • has(value):判断Set中是否存在某个元素,返回Boolean类型
      • clear():清空Set中的所有的元素,没有返回值
      • forEach(callback, [,thisArg]):通过forEach遍历Set

WeakSet

![[Pasted image 20240810145329.png]]
![[Pasted image 20240810145349.png]]

Map

![[Pasted image 20240810150140.png]]
![[Pasted image 20240810150225.png]]

WeakMap

![[Pasted image 20240810152228.png]]
![[Pasted image 20240810152332.png]]

Promise

  • 用于处理异步的解决方案
  1. Promise 的状态: Promise 有三种状态:Pending(进行中)、Fulfilled(已成功)和 Rejected(已失败)。这些状态可以帮助我们更好地跟踪异步操作的进度。

  2. Promise 的链式调用: 通过 .then() 和 .catch() 方法,我们可以将多个异步操作串联起来,形成一个 Promise 链。这样可以使代码更加清晰和可读。

  3. 错误处理: 在 Promise 链中,只需在最后添加一个 .catch() 方法即可捕获任何一个步骤中出现的错误,大大简化了错误处理的逻辑。

  4. async/await: 为了进一步简化 Promise 的使用,ES2017 引入了 async/await 语法糖。使用 async 函数可以让异步代码看起来更像同步代码,大大提高了可读性。

  • 使用基本格式:
  1. 创建 Promise:
    • 使用 new Promise() 创建一个新的 Promise 对象。
    • 传递一个函数作为参数,这个函数被称为 Promise 执行器(Executor)。
    • 在 Promise 执行器函数内部,我们执行异步操作。
    • 如果异步操作成功,调用 resolve(result) 函数,将结果传递出去。
    • 如果异步操作失败,调用 reject(error) 函数,将错误信息传递出去。
  2. 使用 Promise:
    • 通过 .then() 方法处理 Promise 成功的情况。
    • 通过 .catch() 方法处理 Promise 失败的情况。
    • 通过 .finally() 方法处理无论成功还是失败都要执行的代码。
  3. Promise 链式调用:
    • 每次调用 .then() 或 .catch() 方法都会返回一个新的 Promise 对象。
    • 可以将多个 Promise 操作串联起来,形成 Promise 链。
    • 下一个 .then() 方法会等待上一个 Promise 完成后再执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
new Promise((resolve, reject) => {
// 执行一些异步操作
if (/* 操作成功 */) {
resolve(result);
} else {
reject(error);
}
})
.then(result => {
// 处理成功的结果
})
.catch(error => {
// 处理失败的情况
})
.finally(() => {
// 无论成功还是失败都会执行的代码
});
  • resolve不同值的区别
  1. 传递普通值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    new Promise((resolve, reject) => {
    resolve('success');
    })
    .then(result => {
    console.log(result); // 输出: 'success'
    })
    .catch(error => {
    console.error(error);
    });
    • 如果 resolve() 函数传递一个普通值,这个值会作为 Promise 的 resolve 结果被传递到后续的 .then() 方法中。
  2. 传递 Promise 对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    new Promise((resolve, reject) => {
    resolve(
    new Promise((innerResolve, innerReject) => {
    setTimeout(() => {
    innerResolve('inner promise result');
    }, 2000);
    })
    );
    })
    .then(result => {
    console.log(result); // 输出: 'inner promise result'
    })
    .catch(error => {
    console.error(error);
    });
    • 如果 resolve() 函数传递的是另一个 Promise 对象,那么外层 Promise 的状态会跟随内层 Promise 的状态进行变化。
    • 外层 Promise 会”等待”内层 Promise 完成,然后采用内层 Promise 的状态和结果。
  3. 传递 thenable 对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const myThenable = {
    then(resolve, reject) {
    resolve('thenable result');
    }
    };

    new Promise((resolve, reject) => {
    resolve(myThenable);
    })
    .then(result => {
    console.log(result); // 输出: 'thenable result'
    })
    .catch(error => {
    console.error(error);
    });
    • 如果 resolve() 函数传递的是一个具有 .then() 方法的对象(称为 thenable 对象),那么 Promise 会”等待”这个对象执行完 .then() 方法,并采用其返回的结果。
  4. 传递 throw 或 reject()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    new Promise((resolve, reject) => {
    reject(new Error('Something went wrong'));
    })
    .then(result => {
    console.log(result);
    })
    .catch(error => {
    console.error(error); // 输出: Error: Something went wrong
    });
    • 如果在 Promise 执行器函数内部调用 throw 或 reject() 函数,那么 Promise 的状态会变为 rejected
    • 后续的 .catch() 方法会捕获到这个错误,并进行处理。

ES8~ES13

ES8中对象的相关属性

  1. Object.values():
    • 该方法返回一个给定对象自身的所有可枚举属性值的数组。
    • 它的行为与 Object.keys() 类似,但返回的是属性值,而不是属性名。
  2. Object.entries():
    • 该方法返回一个给定对象自身可枚举字符串键属性 [key, value] 的数组。
    • 这个方法提供了一种迭代一个对象的所有属性的便捷方式。
  3. Object.getOwnPropertyDescriptors():
    • 该方法返回指定对象所有自身属性的描述符。
    • 这个方法在 Object.create() 时很有用,用于实现属性的复制和继承。
  4. Trailing commas:
    • Trailing commas 允许在对象字面量、数组字面量、函数参数列表和函数调用中使用尾随逗号。
    • 这可以使代码更容易维护和扩展,因为添加新属性或参数不会影响前面的代码。
  5. Async functions:
    • Async functions 是 ES8 中引入的一个重要特性,用于简化异步编程。
    • Async 函数返回一个 Promise 对象,可以使用 await 关键字来等待 Promise 完成。

ES8-padStart和padEnd

  • padStart(targetLength, [padString]) 方法将当前字符串填充到指定的长度。填充从字符串的开始(左侧)应用的。
1
2
3
4
5
// 用 '0' 填充到长度 5
console.log('42'.padStart(5, '0')); // Output: '00042'

// 用空格填充到长度 10
console.log('foo'.padStart(10)); // Output: ' foo'
  • padEnd(targetLength, [padString]) 方法将当前字符串填充到指定的长度。填充从字符串的末尾(右侧)应用的。
1
2
3
4
5
// 用 '.' 填充到长度 5
console.log('hello'.padEnd(5, '.')); // Output: 'hello.'

// 用空格填充到长度 10
console.log('foo'.padEnd(10)); // Output: 'foo '

这些方法在处理金额、日期、编号等数据格式时非常有用,可以帮助我们快速地格式化字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 格式化金额
const amount = 123.45;
console.log(amount.toFixed(2).padStart(10, '0')); // Output: '0000123.45'

// 格式化日期
const date = '2023-5-1';
console.log(date.padStart(10, '0')); // Output: '2023-05-01'

// 隐藏卡号
function hideIdCard(idCard) {
return idCard.slice(-4).padStart(18, '*');
}

console.log(hideIdCard('123456789012345678')); // Output: ***************5678

function hideBankCard(bankCard) {
const length = bankCard.length;
const front = bankCard.slice(0, 4);
const back = bankCard.slice(-4);
return front + '*'.repeat(length - 8) + back;
}

console.log(hideBankCard('1234567890123456')); // Output: 1234************3456
console.log(hideBankCard('123456789012345678')); // Output: 1234************5678

ES9-Object spread operators

  1. Spread 操作符 (...)
    Spread 操作符可以在对象字面量中使用,用于展开一个现有的对象,将其属性复制到新的对象中。
1
2
3
4
const originalObj = { a: 1, b: 2 };
const newObj = { ...originalObj, c: 3 };

console.log(newObj); // Output: { a: 1, b: 2, c: 3 }

在上面的例子中,我们使用 Spread 操作符 ... 将 originalObj 的属性复制到 newObj 中,并添加了一个新的属性 c
2. 合并多个对象
Spread 操作符可以方便地合并多个对象:

1
2
3
4
5
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const mergedObj = { ...obj1, ...obj2 };

console.log(mergedObj); // Output: { a: 1, b: 3, c: 4 }

在这个例子中,我们使用多个 Spread 操作符将 obj1 和 obj2 的属性合并到 mergedObj 中。当有重复的属性时,后面的对象的属性会覆盖前面对象的属性。
3. 与解构赋值结合使用
Spread 操作符也可以与对象解构赋值结合使用:

1
2
3
4
5
const originalObj = { a: 1, b: 2, c: 3 };
const { a, ...rest } = originalObj;

console.log(a); // Output: 1
console.log(rest); // Output: { b: 2, c: 3 }

在这个例子中,我们使用对象解构赋值提取 a 属性,并使用 Spread 操作符将剩余的属性赋值给 rest 对象。
4. 浅拷贝和深拷贝
需要注意的是,Spread 操作符只能进行浅拷贝,如果对象中嵌套了其他对象或数组,则需要使用其他方法实现深拷贝

1
2
3
4
const originalObj = { a: 1, b: { c: 2 } };
const newObj = { ...originalObj };

console.log(newObj.b === originalObj.b); // Output: true (浅拷贝)

ES10-flat flatMap

  1. flat()
    flat() 方法用于将嵌套数组”拉平”为一维数组。它接受一个可选的 depth 参数,指定要提取嵌套数组的深度。如果不传 depth 参数,默认深度为 1。
1
2
3
4
const nestedArray = [1, [2, 3], [4, [5, 6]]];

console.log(nestedArray.flat()); // Output: [1, 2, 3, 4, [5, 6]]
console.log(nestedArray.flat(2)); // Output: [1, 2, 3, 4, 5, 6]

在上面的例子中,第一个 flat() 调用将数组拉平一层,第二个调用将数组拉平两层。
2. flatMap()
flatMap() 是 map() 和 flat() 的组合。它首先使用提供的映射函数映射每个元素,然后将结果压缩成一个新数组。

1
2
3
4
const arr = [1, 2, 3, 4];

const doubledAndFlattened = arr.flatMap(x => [x, x * 2]);
console.log(doubledAndFlattened); // Output: [1, 2, 2, 4, 3, 6, 4, 8]

在上面的例子中,我们使用 flatMap() 将每个元素映射为一个包含原始值和其双倍值的数组,然后将这些数组压缩成一个新数组。
flatMap() 的好处是,它可以在一步操作中完成映射和拉平操作,相比于先使用 map() 再使用 flat(),效率更高。

ES10-Object.fromEntries

  1. Object.entries()
    Object.entries() 方法返回一个给定对象自身可枚举字符串属性 [key, value] 对组成的数组。
1
2
3
4
const obj = { a: 1, b: 2, c: 3 };
const entries = Object.entries(obj);

console.log(entries); // Output: [['a', 1], ['b', 2], ['c', 3]]

这个方法在需要遍历对象属性时非常有用,比如使用 for...of 循环:

1
2
3
4
5
6
7
for (const [key, value] of Object.entries(obj)) {
console.log(key, value);
}
// Output:
// a 1
// b 2
// c 3
  1. Object.fromEntries()
    Object.fromEntries() 方法执行与 Object.entries() 逆向操作,将一个键值对列表转换为一个对象。
1
2
3
4
const entries = [['a', 1], ['b', 2], ['c', 3]];
const obj = Object.fromEntries(entries);

console.log(obj); // Output: { a: 1, b: 2, c: 3 }

这个方法在需要从其他数据结构(如 Map)转换为对象时非常有用:

1
2
3
4
const map = new Map([['a', 1], ['b', 2]]);
const obj = Object.fromEntries(map);

console.log(obj); // Output: { a: 1, b: 2 }

应用场景
![[Pasted image 20240812145943.png]]

ES10-trimStart trimEnd

trim()trimStart() 和 trimEnd() 是 JavaScript 中用于删除字符串两端空白字符的三个方法。

  1. trim()
    trim() 方法返回一个新的字符串,其中从字符串的两端删除了所有空白字符。
1
2
3
const str = "   Hello, world!   ";

console.log(str.trim()); // Output: "Hello, world!"
  1. trimStart()
    trimStart() 方法返回一个新的字符串,其中从字符串的开头删除了所有空白字符。
1
2
3
const str = "   Hello, world!   ";

console.log(str.trimStart()); // Output: "Hello, world! "
  1. trimEnd()
    trimEnd() 方法返回一个新的字符串,其中从字符串的末尾删除了所有空白字符。
1
2
3
const str = "   Hello, world!   ";

console.log(str.trimEnd()); // Output: " Hello, world!"

这三个方法的主要区别在于它们删除空白字符的位置:

  • trim() 删除字符串两端的空白字符
  • trimStart() 删除字符串开头的空白字符
  • trimEnd() 删除字符串末尾的空白字符

ES11-BigInt

要表示大于NUmber.MAX_SAFE_INTEGER的数值,需要在数值后面加上n![[Pasted image 20240812151216.png]]

ES11-Nullish Coalescing Operator (空值合并运算符)

Nullish Coalescing Operator (空值合并运算符) 是 ECMAScript 2020 (ES11) 引入的一个新的运算符,它可以帮助我们处理 null 和 undefined 值。
这个运算符的符号是 ??它的工作原理是:

  1. 如果左侧的操作数不是 null 或 undefined,则返回左侧的操作数。
  2. 如果左侧的操作数是 null 或 undefined,则返回右侧的操作数。
    下面是一些例子:
1
2
3
4
5
6
7
8
9
10
// 基本用法
const username = '';
const displayName = username ?? 'Guest'; // 输出: 'Guest'

const age = 0;
const defaultAge = age ?? 30; // 输出: 0

// 与逻辑OR (||) 运算符的比较
const username2 = '';
const displayName2 = username2 || 'Guest'; // 输出: 'Guest'

在上面的例子中,当 username 为空字符串时,逻辑 OR (||) 运算符将返回 'Guest'。但是,空字符串在 JavaScript 中是一个有效的值,我们可能不希望将其视为”falsy”。这时,Nullish Coalescing Operator 就很有用,它只会在值为 null 或 undefined 时返回右侧操作数。

另一个常见的用例是设置默认值:

1
2
3
4
5
6
7
8
function greet(name) {
const displayName = name ?? 'Guest';
console.log(`Hello, ${displayName}!`);
}

greet(null); // 输出: "Hello, Guest!"
greet(''); // 输出: "Hello, !"
greet('John'); // 输出: "Hello, John!"

在这个例子中,如果 name 参数是 null 或 undefined,Nullish Coalescing Operator 会将 displayName 设置为 'Guest'。但如果 name 是一个空字符串,它仍然会被使用,因为空字符串不是 null 或 undefined

ES11-Optional Chaining (可选链)

Optional Chaining (可选链) 是 ECMAScript 2020 (ES11) 引入的一个新的运算符,用于安全地访问嵌套对象的属性。它的符号是 ?.
Optional Chaining 可以帮助我们避免在访问嵌套对象属性时出现的 TypeError: Cannot read property 'x' of undefined 错误。
下面是一些例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 不使用可选链
let adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};

console.log(adventurer.dog.name); // TypeError: Cannot read property 'name' of undefined

// 使用可选链
let adventurer2 = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};

console.log(adventurer2?.dog?.name); // undefined

在第一个例子中,尝试访问 adventurer.dog.name 会抛出 TypeError,因为 adventurer.dog 是 undefined
但在第二个例子中,使用 Optional Chaining 运算符 ?. 可以安全地访问嵌套属性。如果 adventurer2.dog 是 undefined,则整个表达式的结果也是 undefined,而不会抛出错误。

Optional Chaining 也可以与函数调用一起使用:

1
2
3
4
5
6
7
8
9
10
let customer = {
name: 'Carl',
address: {
city: 'Seattle'
}
};

console.log(customer.address.city); // 'Seattle'
console.log(customer.address?.city); // 'Seattle'
console.log(customer.address?.getZipCode?.()); // undefined

在最后一个例子中,即使 customer.address.getZipCode 不存在,使用 Optional Chaining 也不会抛出错误,而是返回 undefined

ES12-FinalizationRegistry

FinalizationRegistry 是 ECMAScript 2021 (ES12) 引入的一个新的 API,它允许在对象被垃圾回收时执行自定义的清理逻辑。
FinalizationRegistry 的主要用途是:

  1. 监听对象的清理:当一个对象被垃圾回收时,FinalizationRegistry 会通知注册的回调函数。这可以用于执行清理操作,比如释放资源、发送通知等。
  2. 避免内存泄漏:FinalizationRegistry 可以帮助开发者避免内存泄漏,因为它可以确保在对象被销毁时执行清理逻辑。
    示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建FinalizationRegistry实例
const registry = new FinalizationRegistry((value) => {
console.log(`Finalized: ${value}`);
});

// 注册对象
const obj = { id: 1 };
registry.register(obj, 'object-1');

// 手动触发垃圾回收
obj = null;
global.gc(); // 在Node.js中手动触发

// 输出: Finalized: object-1

在上述示例中:

  1. 我们创建了一个 FinalizationRegistry 实例,并传入一个回调函数,该函数将在对象被垃圾回收时被调用。
  2. 我们注册了一个对象 obj 到 FinalizationRegistry,并为其提供了一个标识符 'object-1'
  3. 我们手动将 obj 设置为 null,然后调用 global.gc() 来触发垃圾回收(在 Node.js 中,需要使用 --expose-gc 标志来启用手动垃圾回收)。
  4. 当对象被垃圾回收时,FinalizationRegistry 会调用我们提供的回调函数,并输出 'Finalized: object-1'
    FinalizationRegistry 的一个主要优点是,它不会阻止对象被垃圾回收。相反,它会在对象被回收后执行清理逻辑,这使得它比 WeakMap 和 WeakSet 更加灵活和强大。
    FinalizationRegistry 的典型用例包括:
  • 清理 DOM 元素和事件监听器
  • 关闭数据库连接或释放其他系统资源
  • 发送对象销毁的通知

ES12-WeakRefs

WeakRef 是 ECMAScript 2021 (ES12) 引入的一个新的 API,它允许创建对对象的”弱引用”。这意味着被引用的对象可以被垃圾回收器回收,即使还有 WeakRef 引用它。
主要用途:

  1. 避免内存泄漏: 由于 WeakRef 不会妨碍对象被垃圾回收,因此可以帮助开发者避免内存泄漏的问题。
  2. 缓存模式: WeakRef 可以用于实现一种”缓存模式”,在需要时重新创建对象,而不是持有永久引用。
  3. 观察对象生命周期: WeakRef 可以用于观察对象的生命周期,并在对象被垃圾回收时执行相应的清理操作。
    示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 创建一个对象
const obj = { id: 1 };

// 创建一个 WeakRef 实例
const weakRef = new WeakRef(obj);

// 手动触发垃圾回收
obj = null;
global.gc(); // 在Node.js中手动触发

// 尝试访问 WeakRef 持有的对象
const refObj = weakRef.deref();
if (refObj) {
console.log(refObj.id); // 输出 1
} else {
console.log('Object has been garbage collected');
}
  1. 我们创建了一个对象 obj
  2. 我们使用 new WeakRef(obj) 创建了一个 WeakRef 实例,它持有对 obj 的引用。
  3. 我们手动将 obj 设置为 null,然后调用 global.gc() 来触发垃圾回收(在 Node.js 中,需要使用 --expose-gc 标志来启用手动垃圾回收)。
  4. 我们尝试使用 weakRef.deref() 方法访问 WeakRef 持有的对象。如果对象还存在,则输出 1。否则,输出 'Object has been garbage collected'

WeakRef 的一个主要特点是,它不会阻止被引用的对象被垃圾回收。相反,一旦对象没有其他强引用,它就可以被垃圾回收,即使还有 WeakRef 引用它。这使得 WeakRef 非常适合于缓存模式和生命周期观察等用例
WeakRef 通常与 FinalizationRegistry 配合使用,后者可以在对象被垃圾回收时执行清理逻辑。这种组合可以帮助开发者更好地管理内存和系统资源。

ES13-method.at()

method.at() 是 ECMAScript 2022 (ES13) 引入的一个新的数组方法,它允许使用索引值访问数组元素,并且可以接受负值索引。
特点:

  1. 支持负值索引at() 方法可以接受负值索引,这意味着可以从数组末尾开始计数。例如, arr.at(-1) 返回数组的最后一个元素。
  2. 返回 undefined 而不是抛出错误: 如果使用无效的索引值(例如索引超出数组范围),at() 方法不会抛出错误,而是返回 undefined
  3. 更简洁的语法at() 方法提供了一种更简洁的语法来访问数组元素,相比于使用方括号语法 (arr[index]) 更加直观。
    示例:
1
2
3
4
5
6
const arr = [1, 2, 3, 4, 5];

console.log(arr.at(2)); // 输出: 3
console.log(arr.at(-1)); // 输出: 5

console.log(arr.at(10)); // 输出: undefined

ES13-Object.hasOwn(obj, prop)

Object.hasOwn() 是 ECMAScript 2022 (ES13) 引入的一个新的静态方法,它用于检查一个对象是否包含指定的属性。这个方法是 Object.prototype.hasOwnProperty() 方法的一种更简洁的替代方式。
特点:

  1. 更简洁的语法Object.hasOwn(obj, prop) 方法提供了一种更简洁的语法来检查对象是否包含指定的属性,相比于使用 obj.hasOwnProperty(prop) 更加简洁。
  2. 更安全的属性检查Object.hasOwn() 避免了原型污染问题,因为它直接在对象上检查属性,而不会受到原型链上的属性影响。这使得它更安全地用于检查对象属性。
  3. 支持 null 和 undefined: 与 obj.hasOwnProperty(prop) 不同, Object.hasOwn() 可以接受 null 或 undefined 作为第一个参数,并在这种情况下返回 false
    示例:
1
2
3
4
5
6
7
const obj = { name: 'John', age: 30 };

console.log(Object.hasOwn(obj, 'name')); // true
console.log(Object.hasOwn(obj, 'address')); // false

console.log(Object.hasOwn(null, 'name')); // false
console.log(Object.hasOwn(undefined, 'name')); // false

与 obj.hasOwnProperty(prop) 语法相比, Object.hasOwn() 的优势优势:

  • 更简洁的语法,更容易阅读和理解。
  • 避免了原型污染问题,更安全地检查对象属性。
  • 可以处理 null 和 undefined 参数,返回 false

Proxy-Reflect

Proxy基本使用

  • 如果我们希望 监听一个对象的相关操作,那么我们可以 先创建一个代理对象(Proxy对象)
  • 之后通过对代理对象的操作来监听我们想要对原对象进行的操作
    步骤:
  • 首先需要 new Proxy对象,并且传入需要监听的对象以及一个处理对象,称之为handler
    const p = new Proxy(target, handler)
  • 其次, 我们之后的操作都是直接对Proxy的操作,而不是原有的对象,因为我们需要在handler里

Proxy的捕获器

如果想要监听某些具体的操作,就可以在handler中添加对应的 捕获器
![[Pasted image 20240812213843.png]]

  • set函数有4个参数
    • target:目标对象(监听的对象)
    • property:将被设置的属性key
    • value:新属性值
    • receiver:调用的代理对象
  • get函数有3个参数
    • target:目标对象(监听的对象)
    • property:将被设置的属性key
    • receiver:调用的代理对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>

const obj = {
name: "why",
age: 18,
height: 1.88
}

const objProxy = new Proxy(obj, {
set: function(target, key, value) {
console.log(`属性${key}被设置成${value}`)
target[key] = value
},
get: function(target, key) {
console.log(`属性${key}被读取`)
return target[key] // 这里返回target[key],可以让属性的读取操作正常进行
}
})

objProxy.address = "四川省"
console.log(objProxy.address) // 四川省
objProxy.age = 20

</script>

![[Pasted image 20240812215441.png]]

Reflect

  • 作用:Reflect 是 JavaScript 中的一个内置对象,它提供了一组方法和属性,用于更好地反映和操作对象。相比于直接使用对象的属性和方法,Reflect 提供了一些额外的功能和便利。
  • 与Object的区别: Reflect 与 Object 的方法名称很相似,但 Reflect 方法会返回操作结果,而 Object 方法则会返回操作对象本身。![[Pasted image 20240813000221.png]]
  • 常见方法:
  1. 获取属性值:
    • Reflect.get(target, property[, receiver]): 获取对象 target 的属性 property 的值。
  2. 设置属性值:
    • Reflect.set(target, property, value[, receiver]): 设置对象 target 的属性 property 的值为 value
  3. 删除属性:
    • Reflect.deleteProperty(target, property): 删除对象 target 的属性 property
  4. 检查属性是否存在:
    • Reflect.has(target, property): 检查对象 target 是否拥有属性 property
  5. 获取对象原型:
    • Reflect.getPrototypeOf(target): 获取对象 target 的原型。
  6. 设置对象原型:
    • Reflect.setPrototypeOf(target, prototype): 设置对象 target 的原型为 prototype
  7. 判断对象是否可扩展:
    • Reflect.isExtensible(target): 判断对象 target 是否可扩展。
  8. 冻结对象:
    • Reflect.preventExtensions(target): 让对象 target 变为不可扩展。
  9. 获取自身属性描述符:
    • Reflect.getOwnPropertyDescriptor(target, property): 获取对象 target 的属性 property 的描述符。
  10. 定义属性:
    • Reflect.defineProperty(target, property, descriptor): 在对象 target 上定义属性 property
  11. 调用函数:
    • Reflect.apply(target, thisArgument, argumentsList): 使用给定的 this 值和参数列表调用目标函数。
  12. 使用 new 创建实例:
    • Reflect.construct(target, argumentsList[, newTarget]): 使用给定的构造函数 target 和参数列表创建一个新实例。
      ![[Pasted image 20240813000437.png]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<script>

const obj = {
name: "why",
age: 18,
get info() {
return this.name
}
}

const objProxy = new Proxy(obj, {
set(target, key, value, receiver) {

// target[key] = value
// return true
// 好处1、代理对象的目的:不再直接操作原对象
// 好处2、Reflect.set()方法可以返回一个布尔值,表示是否设置成功
/* 好处3、
reciver === objProxy,就是外层的Proxy对象
Reflect.set/get最后一个参数receiver可以决定对象访问器的setter/getter的this指向
*/
console.log("Proxy中的设置方法被调用")
const isSuccess = Reflect.set(target, key, value)

if(!isSuccess) {
throw new Error("set failed")
}
},
get(target, key, receiver) {
console.log("Proxy中的获取方法被调用")
return Reflect.get(target, key, receiver)
}
})

objProxy.name = "kobe"
console.log(objProxy.info)

Promise

异步任务的处理

  • 我们调用一个函数,这个函数中发送网络请求
  • 如果 发送网络请求成功了,那么告知调用者发送成功,并且返回相关数据
  • 如果 发送网络请求失败了,那么告知调用者发送失败,并且告知错误信息![[Pasted image 20240816165910.png]]

Promise的基本使用

  • Promise是一个类,当我们需要的时候,给予调用者一个承诺:待会儿回调函数的时候,就可以创建一个Promise对象
  • 在通过new创建Promise对象时,我们需要传入一个回调函数,称为executor
    • 这个回调函数会被立即执行,并且给传入另外两个回调函数resolve、reject
    • 调用resolve回调函数时,会执行Promise对象的then方法传入的回调函数
    • 调用reject回调函数时,会执行Promise对象的catch方法传入的回调函数![[Pasted image 20240816172919.png]]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script>

function execCode(counter) {
// 异步处理
const promise = new Promise((resolve, reject) => {
// 模拟异步处理
if(counter > 0) {
let total = 0
for(let i = 0; i < counter; i++) {
total += i
}
resolve(total)
} else {
reject(`${counter}输入错误`)
}
})
return promise
}

execCode(5).then(sucessed => {
console.log("成功:", sucessed)
}).catch(error => {
console.log("失败:", error)
})

execCode(-5).then(sucessed => {
console.log("成功:", sucessed)
}).catch(error => {
console.log("失败:", error)
})

</script>

resolve不同值

  1. 传递普通值
    1
    2
    3
    4
    5
    6
    7
    8
    9
    new Promise((resolve, reject) => {
    resolve('success');
    })
    .then(result => {
    console.log(result); // 输出: 'success'
    })
    .catch(error => {
    console.error(error);
    });
    • 如果 resolve() 函数传递一个普通值,这个值会作为 Promise 的 resolve 结果被传递到后续的 .then() 方法中。
  2. 传递 Promise 对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    new Promise((resolve, reject) => {
    resolve(
    new Promise((innerResolve, innerReject) => {
    setTimeout(() => {
    innerResolve('inner promise result');
    }, 2000);
    })
    );
    })
    .then(result => {
    console.log(result); // 输出: 'inner promise result'
    })
    .catch(error => {
    console.error(error);
    });
    • 如果 resolve() 函数传递的是另一个 Promise 对象,那么外层 Promise 的状态会跟随内层 Promise 的状态进行变化。
    • 外层 Promise 会”等待”内层 Promise 完成,然后采用内层 Promise 的状态和结果。
  3. 传递 thenable 对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    const myThenable = {
    then(resolve, reject) {
    resolve('thenable result');
    }
    };

    new Promise((resolve, reject) => {
    resolve(myThenable);
    })
    .then(result => {
    console.log(result); // 输出: 'thenable result'
    })
    .catch(error => {
    console.error(error);
    });
    • 如果 resolve() 函数传递的是一个具有 .then() 方法的对象(称为 thenable 对象),那么 Promise 会”等待”这个对象执行完 .then() 方法,并采用其返回的结果。
  4. 传递 throw 或 reject()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    new Promise((resolve, reject) => {
    reject(new Error('Something went wrong'));
    })
    .then(result => {
    console.log(result);
    })
    .catch(error => {
    console.error(error); // 输出: Error: Something went wrong
    });
    • 如果在 Promise 执行器函数内部调用 throw 或 reject() 函数,那么 Promise 的状态会变为 rejected
    • 后续的 .catch() 方法会捕获到这个错误,并进行处理。

then的返回值

Promise 的 then 方法的返回值是一个新的 Promise 对象。这个返回值可以用于实现链式调用。

  • 关键点
  1. 成功回调的返回值:
    • 如果在 then 的成功回调中返回一个值,这个值将作为下一个 then 的输入。
    • 如果返回的是一个 Promise,下一个 then 将等待该 Promise 完成。
  2. 失败回调的返回值:
    • 如果在 then 的失败回调中返回一个值,该值将被忽略,链中的下一个 then 将继续执行。
    • 如果返回的是一个 Promise,同样会等待下一个 Promise 完成。
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const promise = new Promise((resolve, reject) => {
resolve(42);
});

promise
.then(result => {
console.log(result); // 输出: 42
return result + 1; // 返回 43
})
.then(result => {
console.log(result); // 输出: 43
return Promise.resolve(result + 1); // 返回一个新的 Promise
})
.then(result => {
console.log(result); // 输出: 44
});

catch的返回值

Promise 的 catch 方法的返回值也会是一个新的 Promise 对象,具有和 then 方法相似的特性。具体来说,catch 主要用于处理链中的错误,但是它的返回值可以影响后续的链式调用。

  • 关键点
  1. 处理错误并返回值:
    • 如果 catch 处理了错误并返回一个值,这个值将作为后续链中下一个 then 的输入。
  2. 返回新 Promise:
    • 如果在 catch 中返回一个 Promise,下一个 then 会等待这个 Promise 完成,然后将结果传递下去。
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const promise = new Promise((resolve, reject) => {
reject('出错了');
});

promise
.then(result => {
console.log(result); // 不会执行
})
.catch(error => {
console.error(error); // 输出: 出错了
return '处理完毕'; // 返回一个字符串
})
.then(result => {
console.log(result); // 输出: 处理完毕
});
  • 如果我们希望后续继续执行catch,那么需要抛出一个异常![[Pasted image 20240816202837.png]]

finally的回调

Promise 的 finally 方法用于在 Promise 操作结束后执行一个回调函数,无论是成功还是失败。这使得 finally 非常适合用于清理或执行一些始终需要进行的操作,比如关闭文件、清理资源等。

  • 特点
  1. 不影响链的结果:
    • finally 的回调不会接收 Promise 的结果或错误。
    • 无论前面的 then 或 catch 是否成功,finally 始终会执行。
  2. 返回值:
    • finally 返回一个新的 Promise,其解析方式与前面的 Promise 一致。
    • 如果在 finally 中返回一个值或 Promise,将不会影响前面 then 或 catch 的结果。
  • 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const promise = new Promise((resolve, reject) => {
const success = true; // 模拟操作成功或失败

if (success) {
resolve('成功');
} else {
reject('失败');
}
});

promise
.then(result => {
console.log(result); // 输出: 成功
})
.catch(error => {
console.error(error); // 不会执行
})
.finally(() => {
console.log('清理操作'); // 输出: 清理操作
});

Promise类方法

Promise 的类方法提供了用于创建和操作 Promise 实例的多种功能。以下是主要的 Promise 类方法及其描述:

1. Promise.resolve(value)

  • 功能: 返回一个以给定值解析后的 Promise 对象。
  • 示例:
1
2
const promise1 = Promise.resolve(42);
promise1.then(value => console.log(value)); // 输出: 42

2. Promise.reject(reason)

  • 功能: 返回一个以给定原因拒绝后的 Promise 对象。
  • 示例:
1
2
const promise2 = Promise.reject('出错了');
promise2.catch(error => console.error(error)); // 输出: 出错了

3. Promise.all(iterable)

  • 功能: 接受一个可迭代对象(如数组),当所有 Promise 都已成功时,返回一个新的 Promise,并解析为一个数组,包含所有 Promise 的结果。如果其中任何一个 Promise 被拒绝,它将立即返回拒绝状态。
  • 示例:
1
2
3
4
5
6
7
8
9
const promise3 = Promise.all([
Promise.resolve(1),
Promise.resolve(2),
Promise.resolve(3)
]);

promise3.then(values => {
console.log(values); // 输出: [1, 2, 3]
});

4. Promise.allSettled(iterable)

  • 功能: 接受一个可迭代对象,返回一个新的 Promise,当所有 Promise 的状态都已确定时(不论成功还是失败),以数组的形式解析,数组中的每个对象描述了每个 Promise 的结果。
  • 示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const promise4 = Promise.allSettled([
Promise.resolve(1),
Promise.reject('失败'),
Promise.resolve(3)
]);

promise4.then(results => {
console.log(results);
// 输出: [
// { status: 'fulfilled', value: 1 },
// { status: 'rejected', reason: '失败' },
// { status: 'fulfilled', value: 3 }
// ]
});

5. Promise.any(iterable)

  • 功能: 接受一个可迭代对象,返回一个新的 Promise。只要有一个 Promise 成功,它就会解析为那个成功的值。如果所有 Promise 都被拒绝,则返回一个拒绝的 Promise,其理由是一个 AggregateError
  • 示例:
1
2
3
4
5
6
7
8
9
const promise5 = Promise.any([
Promise.reject('失败1'),
Promise.resolve(3),
Promise.reject('失败2')
]);

promise5.then(value => {
console.log(value); // 输出: 3
});

6. Promise.race(iterable)

  • 功能: 接受一个可迭代对象,返回一个新的 Promise,只要有一个 Promise 完成或被拒绝,返回的 Promise 就会返回这个完成或被拒绝的值。
  • 示例:
1
2
3
4
5
6
7
8
const promise6 = Promise.race([
new Promise((resolve) => setTimeout(() => resolve('快速完成'), 100)),
new Promise((resolve) => setTimeout(() => resolve('慢完成'), 200))
]);

promise6.then(value => {
console.log(value); // 输出: 快速完成
});

迭代器和生成器

什么是迭代器

![[Pasted image 20240816214845.png]]![[Pasted image 20240816214905.png]]![[Pasted image 20240817154234.png]]

自定义可迭代对象

![[Pasted image 20240817155356.png]]

可迭代对象的应用

![[Pasted image 20240817162522.png]]

什么是生成器

![[Pasted image 20240817191031.png]]

生成器函数参数返回值

  • 在中间位置直接return
1
2
3
4
5
6
7
8
9
10
11
12
13
function* generatorFunction() {
yield 1;
yield 2;
return '结束'; // 在这里返回,终止生成器
yield 3; // 此行不会被执行
}

const gen = generatorFunction();

console.log(gen.next()); // 输出: { value: 1, done: false }
console.log(gen.next()); // 输出: { value: 2, done: false }
console.log(gen.next()); // 输出: { value: '结束', done: true }
console.log(gen.next()); // 输出: { value: undefined, done: true }
  • 给函数每次执行的时候传入参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script>

function* foo(name1) {
console.log('内部:111');
console.log('内部:222');
const name2 = yield "aaaa"

console.log('内部:333');
console.log('内部:444');
const name3 = yield "bbbb"
console.log('内部:555');
console.log('内部:666');
yield "cccc"
return undefined
}

const bar = foo("next1")

console.log(bar.next()) // {value: "aaaa", done: false}
console.log(bar.next("next2")) // {value: "bbbb", done: false}
console.log(bar.next("next3")) // {value: "cccc", done: true}

</script>

生成器函数提前结束

    1. 使用 return 语句
1
2
3
4
5
6
7
8
9
10
11
12
13
function* generatorFunction() {
yield 1;
yield 2;
return '结束'; // 提前结束
yield 3; // 不会被执行
}

const gen = generatorFunction();

console.log(gen.next()); // 输出: { value: 1, done: false }
console.log(gen.next()); // 输出: { value: 2, done: false }
console.log(gen.next()); // 输出: { value: '结束', done: true }
console.log(gen.next()); // 输出: { value: undefined, done: true }
    1. 使用 throw 语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function* generatorFunction() {
try {
yield 1;
yield 2;
} catch (e) {
console.log(e); // 捕获错误
}
yield 3; // 如果被抛出错误,将不会执行
}

const gen = generatorFunction();

console.log(gen.next()); // 输出: { value: 1, done: false }
console.log(gen.next()); // 输出: { value: 2, done: false }
gen.throw('捕获错误'); // 输出: 捕获错误
console.log(gen.next()); // 输出: { value: undefined, done: true }
    1. 使用 return 结合 next()
1
2
3
4
5
6
7
8
9
10
function* generatorFunction() {
yield 1;
yield 2;
}

const gen = generatorFunction();

console.log(gen.next()); // 输出: { value: 1, done: false }
console.log(gen.return('提前结束')); // 输出: { value: '提前结束', done: true }
console.log(gen.next()); // 输出: { value: undefined, done: true }

生成器替代迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script>

function* foo(start, end) {
for(let i = start; i <= end; i++) {
yield i
}
}

const gen = foo(1, 10)
console.log(gen.next().value)
console.log(gen.next())
console.log(gen.next())
console.log(gen.next())
console.log(gen.next())
console.log(gen.next())
console.log(gen.next())
console.log(gen.next())
console.log(gen.next())
console.log(gen.next())

const names = ['Alice', 'Bob', 'Charlie', 'David']
function* nameGenerator() {
yield* names
}

const nameGen = nameGenerator()
console.log(nameGen.next().value)
console.log(nameGen.next().value)
console.log(nameGen.next().value)
console.log(nameGen.next().value)

</script>

异步处理方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<script>

/*
需求:
1、发送一次网络请求,等到这次网络请求的结果
2、再发送一次网络请求,并将这次网络请求的结果作为参数,再次发送网络请求
3、再次发送网络请求,并将这次网络请求的结果作为参数,再次发送网络请求
4、最后一次的网络请求的结果作为最终结果
*/
// 异步请求代码结构
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url)
}, 2000);
})
}

// 方法一:回调地域
// function getData() {
// requestData("why").then(res1 => {
// console.log("第一次结果:", res1)
// requestData(res1 + "aaa").then(res2 => {
// console.log("第二次结果:", res2)
// requestData(res2 + "bbb").then(res3 => {
// console.log("第三次结果:", res3)
// })
// })
// })
// }

// 方法二:使用Promise进行重构(解决回调地狱)
// function getData() {
// requestData("why").then(res1 => {
// console.log("第一次结果:", res1)
// return requestData(res1 + "aaa")
// }).then(res2 => {
// console.log("第二次结果:", res2)
// return requestData(res2 + "bbb")
// }).then(res3 => {
// console.log("第三次结果:", res3)
// requestData(res3 + "ccc")
// })
// }

// 方法三:使用生成器进行重构
// function* getData() {
// const res1 = yield requestData("why")
// console.log("第一次结果:", res1)
// const res2 = yield requestData(res1 + "aaa")
// console.log("第二次结果:", res2)
// const res3 = yield requestData(res2 + "bbb")
// console.log("第三次结果:", res3)
// }

// const gen = getData()
// gen.next().value.then(res1 => {
// gen.next(res1).value.then(res2 => {
// gen.next(res2).value.then(res3 => {
// gen.next(res3)
// })
// })
// })

// 方法四:使用async/await进行重构
async function getData() {
const res1 = await requestData("why")
console.log("第一次结果:", res1)
const res2 = await requestData(res1 + "aaa")
console.log("第二次结果:", res2)
const res3 = await requestData(res2 + "bbb")
console.log("第三次结果:", res3)
}
getData()

</script>

async、await - 队列

异步函数async function

await关键字

![[Pasted image 20240819154154.png]]
处理异步请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>

function requestData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("error message")
}, 2000);
})
}

async function getData() {
const res1 = await requestData("why")
console.log(res1)
const res2 = await requestData("how")
console.log(res2)
}

getData().catch(err => {
console.log(err)
})

</script>

进程和线程

![[Pasted image 20240819193235.png]]

浏览器中的JavaScript线程

![[Pasted image 20240819205046.png]]

浏览器的事件循环

![[Pasted image 20240819205801.png]]

微任务和宏任务

![[Pasted image 20240819210919.png]]

Promise面试题

1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<script>

console.log("script start")
setTimeout(function () {
console.log("setTimeout1")
new Promise(function (resolve) {
resolve()
}).then(function () {
new Promise(function (resolve) {
resolve()
}).then(function () {
console.log("then4")
})
console.log("then2")
})
})

new Promise(function (resolve) {
console.log("promise1")
resolve()
}).then(function () {
console.log("then1")
})

setTimeout(function () {
console.log("setTimeout2")
})

console.log(2)

queueMicrotask(() => {
console.log("queueMicrotask1")
})

new Promise(function (resolve) {
resolve()
}).then(function () {
console.log("then3")
})

console.log("script end")
/*
代码执行顺序:
script start
promise1
2
script end

then1
queueMicrotask1
then3

setTimeout1
then2
then4

setTimeout2
*/

</script>

2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<script>

async function async1 () {
console.log('async1 start')
await async2()
console.log('async1 end')
}

async function async2 () {
console.log("async2")
}

console.log('script start')

setTimeout(function () {
console.log('setTimeout')
}, 0)

async1()

new Promise (function (resolve) {
console.log('promise1')
resolve()
}).then(function () {
console.log('promise2')
})

console.log('script end')

/*
代码执行顺序:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
*/

</script>

异常处理

throw关键字

![[Pasted image 20240820191231.png]]

Error类型

![[Pasted image 20240820191257.png]]
![[Pasted image 20240820191347.png]]

异常的捕获

![[Pasted image 20240820191801.png]]

Storage和正则表达式

认识Storage

![[Pasted image 20240821160128.png]]

Storage的基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>

// token操作
let token = localStorage.getItem("token")
if(!token) {
console.log("从服务器获取token")
token = "aaaaaaaaa"
localStorage.setItem("token", token)
}

// 用户名和密码操作
let username = localStorage.getItem("username")
let password = localStorage.getItem("password")
if(!username || !password) {
console.log("从服务器获取用户名和密码")
username = "admin"
password = "123456"
localStorage.setItem("username", username)
localStorage.setItem("password", password)
}

console.log(token)

</script>

localStorage和sessionStorage的区别

![[Pasted image 20240821160903.png]]

Storage工具封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Cache {
constructor(isLoacl = true) {
this.storage = isLocal ? localStorage : sessionStorage
}

setCache(key, value) {
if(!value) {
throw new Error("value error: value 必须有值")
}

if(value) {
this.storage.setItem(key, JSON.stringify(value))
}
}

getCache(key) {
const res = this.storage.getItem(key)
if (res) {
return JSON.parse(res)
}
}

removeCache(key) {
this.storage.removeItem(key)
}

clear() {
this.storage.clear()
}
}

const localCache = new Cache(true)
const sessionCache = new Cache(false)

正则表达式

  • 使用单个字符串来描述、匹配一系列句法规则的字符串,是一种字符串匹配利器,可以帮助我们搜索、获取、替代字符串
  • 正则表达式主要由两部分组成:模式和修饰符![[Pasted image 20240821170906.png]]

正则表达式的使用方法

  • JavaScript的正则表达式被用于RegExp的exec和test方法,String的match、matchAll、replace、search、split方法![[Pasted image 20240821172016.png]]![[Pasted image 20240821173022.png]]
    ![[Pasted image 20240821173046.png]]

修饰符flag的使用

![[Pasted image 20240821173451.png]]

正则表达式规则

字符类

![[Pasted image 20240821174044.png]]

锚点

![[Pasted image 20240821182149.png]]

转义

![[Pasted image 20240821193427.png]]

集合

![[Pasted image 20240821193816.png]]

量词

![[Pasted image 20240821195144.png]]
![[Pasted image 20240821195046.png]]

贪婪和惰性

![[Pasted image 20240821200334.png]]
![[Pasted image 20240821200355.png]]

捕获组

![[Pasted image 20240821201218.png]]
![[Pasted image 20240821201443.png]]
![[Pasted image 20240821201509.png]]

正则练习-歌词解析

![[Pasted image 20240822152859.png]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function parseLyric(lyricstring) {
// 根据\n切割字符串
const lyricLineStrings = lyricstring.split("\n")
// console.log(lyricLineStrings)

// 根据每一行歌词进行解析
const timeRe = /\[(\d{2}):(\d{2})\.(\d{2,3})\]/i
const lyricInfos = []
for (const lineString of lyricLineStrings) {
// 获取时间
const result = lineString.match(timeRe)
if(!result) continue
const minuteTime = result[1] * 60 * 1000
const secondTime = result[2] * 1000
const millisecondTime = result[3].length === 3? result[3] : result[3] * 10
const time = minuteTime + secondTime + millisecondTime

// 获取歌词
const content = lineString.replace(timeRe, "").trim()

// 保存歌词信息
lyricInfos.push({
time,
content
})
}

return lyricInfos
}

正则练习-时间格式化

![[Pasted image 20240822161914.png]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<h2 class="time"></h2>

<script>

function formatTime(timestamp, fmtString) {
// 将时间戳转化为Date
const date = new Date(timestamp)

// 将正则和值匹配
const dateO = {
"y+": date.getFullYear(),
"M+": date.getMonth() + 1,
"d+": date.getDate(),
"h+": date.getHours(),
"m+": date.getMinutes(),
"s+": date.getSeconds(),
}

for (const key in dateO) {
const keyRe = new RegExp(key) // 构造正则表达式
// 如果正则表达式匹配到fmtString中,则替换
if(keyRe.test(fmtString)) {
const value = (dateO[key] + "").padStart(2, "0") // 补零
fmtString = fmtString.replace(keyRe, value) // 替换
}
}

return fmtString
}

const timeEl = document.querySelector('.time')
const productJSON = {
name : "iphone",
newPrice : 10000,
oldPrice : 8000,
endTime : 1659252290626
}
timeEl.textContent = formatTime(productJSON.endTime, "yyyy/MM/dd hh:mm:ss")

</script>

防抖、节流、深拷贝、事件总线

防抖函数

认识防抖debounce函数

![[Pasted image 20240822171403.png]]

手动实现防抖函数

  • 基本实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
function yy_debounce(fn, delay) {
let timer = null

const _debounce = () => {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn()
timer = null
}, delay)
}
return _debounce
}
</script>
<script>

const inputEl = document.querySelector("input")

let cnt = 1
inputEl.oninput = yy_debounce(function() {
console.log(`第${cnt++}次输入`)
}, 1000)

</script>
  • 绑定this
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
function yy_debounce(fn, delay) {
let timer = null

const _debounce = function(...args) {
if(timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args) //这里的this绑定到inputEl
timer = null
}, delay)
}
return _debounce
}
</script>
<script>

const inputEl = document.querySelector("input")

let cnt = 1
inputEl.oninput = yy_debounce(function() {
console.log(`第${cnt++}次输入`)
}, 1000)

</script>
  • 实现取消功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<script>
function yy_debounce(fn, delay) {
// 用于记录上一次事件触发的timer
let timer = null

// 触发事件时执行的函数
const _debounce = function(...args) {
// 如果有再次触发事件,那么取消上一次的事件
if(timer) clearTimeout(timer)

// 延迟去执行对应 fn函数
timer = setTimeout(() => {
fn.apply(this, args) //这里的this绑定到inputEl
timer = null
}, delay)
}

// 给_debounce添加一个cancel方法,用于取消防抖
_debounce.cancel = function() {
if(timer) clearTimeout(timer)
}

return _debounce
}
</script>
<script>

const inputEl = document.querySelector("input")

let cnt = 1
inputEl.oninput = yy_debounce(function(event) {
console.log(`第${cnt++}次输入`)
}, 1000)

</script>
  • 立即执行功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<script>
function yy_debounce(fn, delay, immediate = true) {
// 用于记录上一次事件触发的timer
let timer = null
let isInvoke = false // 是否立即执行

// 触发事件时执行的函数
const _debounce = function(...args) {
// 如果有再次触发事件,那么取消上一次的事件
if(timer) clearTimeout(timer)

// 如果是立即执行,那么立即执行对应 fn函数
if(immediate && !isInvoke) {
fn.apply(this, args)
isInvoke = true
return
}

// 延迟去执行对应 fn函数
timer = setTimeout(() => {
fn.apply(this, args) //这里的this绑定到inputEl
// 重置timer和isInvoke
timer = null
isInvoke = false
}, delay)
}

// 给_debounce添加一个cancel方法,用于取消防抖
_debounce.cancel = function() {
if(timer) clearTimeout(timer)
// 重置timer和isInvoke
timer = null
isInvoke = false
}

return _debounce
}
</script>
<script>

const inputEl = document.querySelector("input")

let cnt = 1
inputEl.oninput = yy_debounce(function(event) {
console.log(`第${cnt++}次输入:`, this, event)
}, 1000)

</script>
  • 获取返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<script>
function yy_debounce(fn, delay, immediate = true, resultCallback) {
// 用于记录上一次事件触发的timer
let timer = null
let isInvoke = false // 是否立即执行

// 触发事件时执行的函数
const _debounce = function(...args) {
return new Promise((resolve, reject) => {
try {
// 如果有再次触发事件,那么取消上一次的事件
if(timer) clearTimeout(timer)

let res = undefined
// 如果是立即执行,那么立即执行对应 fn函数
if(immediate && !isInvoke) {
res = fn.apply(this, args)
if (resultCallback) resultCallback(res)
resolve(res)
isInvoke = true
return
}

// 延迟去执行对应 fn函数
timer = setTimeout(() => {
res = fn.apply(this, args) //这里的this绑定到inputEl
if (resultCallback) resultCallback(res)
resolve(res)
// 重置timer和isInvoke
timer = null
isInvoke = false
}, delay);
} catch (error) {
reject(error)
}
})
}
// 给_debounce添加一个cancel方法,用于取消防抖
_debounce.cancel = function() {
if(timer) clearTimeout(timer)
// 重置timer和isInvoke
timer = null
isInvoke = false
}
return _debounce
}
</script>
<script>

const inputEl = document.querySelector("input")

const myDebounce = yy_debounce(function(name, age, height) {
console.log(`name: ${name}, age: ${age}, height: ${height}`)
return "jojo"
}, 1000, false)

myDebounce("jojo", 20, 1.80).then(res => {
console.log(res)
})

</script>

可以用underscore库

节流函数

认识节流throttle函数

![[Pasted image 20240825101157.png]]

手动实现节流函数

  • 实现思路
    ![[Pasted image 20240825103812.png]]
  • 基本实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>

function yy_throttle(fn, interval) {
let startTime = 0

const _throttle = function() {
const noeTime = new Date().getTime()
const waitTime = interval - (noeTime - startTime)
if(waitTime <= 0) {
fn()
startTime = noeTime
}
}

return _throttle
}

</script>

<script>

const inputEl = document.querySelector('input')
inputEl.oninput = yy_throttle(function() {
console.log('input')
}, 1000)

</script>
  • this绑定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>

function yy_throttle(fn, interval) {
let startTime = 0

const _throttle = function(...args) {
const noeTime = new Date().getTime()
const waitTime = interval - (noeTime - startTime)
if(waitTime <= 0) {
fn.apply(this, args)
startTime = noeTime
}
}

return _throttle
}

</script>

<script>

const inputEl = document.querySelector('input')
inputEl.oninput = yy_throttle(function(event) {
console.log('input:',this.value, event)
}, 1000)

</script>
  • 控制立即执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<script>

function yy_throttle(fn, interval, leading = false) {
let startTime = 0

const _throttle = function(...args) {
// 获取当前时间
const nowTime = new Date().getTime()

// 对立即执行函数进行控制
if(!leading && startTime === 0) {
startTime = nowTime
}

const waitTime = interval - (nowTime - startTime)
if(waitTime <= 0) {
fn.apply(this, args)
startTime = nowTime
}
}

return _throttle
}

</script>

<script>

const inputEl = document.querySelector('input')
inputEl.oninput = yy_throttle(function(event) {
console.log('input:',this.value, event)
}, 1000)

</script>
  • 尾部执行控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<script>

function yy_throttle(fn, interval, {leading = false, trailing = true} = {}) {
let startTime = 0
let timer = null

const _throttle = function(...args) {
// 获取当前时间
const nowTime = new Date().getTime()

// 对立即执行函数进行控制
if(!leading && startTime === 0) {
startTime = nowTime
}

const waitTime = interval - (nowTime - startTime)
if(waitTime <= 0) {
if (timer) clearTimeout(timer)
fn.apply(this, args)
startTime = nowTime
timer = null
return
}

// 节流函数尾部执行控制
if (trailing && !timer) {
timer = setTimeout(() => {
fn.apply(this, args)
startTime = new Date().getTime()
timer = null
}, waitTime)
}
}

return _throttle
}

</script>

<script>

const inputEl = document.querySelector('input')
inputEl.oninput = yy_throttle(function(event) {
console.log('input:',this.value, event)
}, 1000, {trailing: true})

</script>
  • 取消尾部功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<script>

function yy_throttle(fn, interval, {leading = false, trailing = true} = {}) {
let startTime = 0
let timer = null

const _throttle = function(...args) {
// 获取当前时间
const nowTime = new Date().getTime()

// 对立即执行函数进行控制
if(!leading && startTime === 0) {
startTime = nowTime
}

const waitTime = interval - (nowTime - startTime)
if(waitTime <= 0) {
if (timer) clearTimeout(timer)
fn.apply(this, args)
startTime = nowTime
timer = null
return
}

// 节流函数尾部执行控制
if (trailing && !timer) {
timer = setTimeout(() => {
fn.apply(this, args)
startTime = new Date().getTime()
timer = null
}, waitTime)
}
}

_throttle.cancel = function() {
if(timer) clearTimeout(timer)
startTime = 0
timer = null
}

return _throttle
}

</script>

<script>

const inputEl = document.querySelector('input')
const buttonEl = document.querySelector('button')
inputEl.oninput = yy_throttle(function(event) {
console.log('input:',this.value, event)
}, 1000, {trailing: true})

buttonEl.onclick = function() {
inputEl.oninput.cancel()
}
</script>
  • 获取返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<script>

function yy_throttle(fn, interval, {leading = false, trailing = true} = {}) {
let startTime = 0
let timer = null

const _throttle = function(...args) {
// 获取当前时间
return new Promise((resolve, reject) => {
try {
const nowTime = new Date().getTime()

// 对立即执行函数进行控制
if(!leading && startTime === 0) {
startTime = nowTime
}

const waitTime = interval - (nowTime - startTime)
if(waitTime <= 0) {
if (timer) clearTimeout(timer)
const res = fn.apply(this, args)
resolve(res)
startTime = nowTime
timer = null
return
}

// 节流函数尾部执行控制
if (trailing && !timer) {
timer = setTimeout(() => {
const res = fn.apply(this, args)
resolve(res)
startTime = new Date().getTime()
timer = null
}, waitTime);
}
}
catch (error) {
reject(error)
}
})
}

_throttle.cancel = function() {
if(timer) clearTimeout(timer)
startTime = 0
timer = null
}

return _throttle
}

</script>

<script>

const inputEl = document.querySelector('input')
const buttonEl = document.querySelector('button')
inputEl.oninput = yy_throttle(function(event) {
console.log('input:',this.value, event)
return "jojo"
}, 1000, {trailing: true})

buttonEl.onclick = function() {
inputEl.oninput.cancel()
}
</script>

深拷贝

  • 深拷贝函数的基本使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<script>

function isObject(obj) {
const objType = typeof obj
return (obj !== null) && (objType === "object" || objType === "function")
}

// 深拷贝函数
function deepCopy(obj) {
if(!isObject(obj)) {
return obj
}

const newObj = {}
for (const key in obj) {
newObj[key] = deepCopy(obj[key])
}
return newObj
}

const info = {
name: "jojo",
age: 20,
friend: {
name: "小羊",
age: 19,
address: {
name: "四川",
detail: "成都市"
}
}
}

const newObj = deepCopy(info)

console.log(newObj)

</script>
  • 区分数组与对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<script>

function isObject(obj) {
const objType = typeof obj
return (obj !== null) && (objType === "object" || objType === "function")
}

// 深拷贝函数
function deepCopy(obj) {
if(!isObject(obj)) {
return obj
}

const newObj = Array.isArray(obj) ? [] : {}
for (const key in obj) {
newObj[key] = deepCopy(obj[key])
}
return newObj
}

const info = {
name: "jojo",
age: 20,
friend: {
name: "小羊",
age: 19,
address: {
name: "四川",
detail: "成都市"
},
cnt: [1, 2, 3]
}
}

const newObj = deepCopy(info)

console.log(newObj)

</script>
  • 其他类型处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<script>

function isObject(obj) {
const objType = typeof obj
return (obj !== null) && (objType === "object" || objType === "function")
}

// 深拷贝函数
function deepCopy(obj) {

// symbol类型
if (typeof obj === "symbol") {
return Symbol(obj.description)
}

if(!isObject(obj)) {
return obj
}

// set类型
if(obj instanceof Set) {
const newSet = new Set()
for(const setItem of obj) {
newSet.add(deepCopy(setItem))
}
return newSet
}

// function类型,不需要深拷贝
if(typeof obj === "function") {
return obj
}

// key为symbol类型
const symbolKeys = Object.getOwnPropertySymbols(obj)
for(const symbolKey of symbolKeys) {
obj[symbolKey] = deepCopy(obj[symbolKey])
}

// 如果是对象类型
const newObj = Array.isArray(obj) ? [] : {}
map.set(obj, newObj)
for (const key in obj) {
newObj[key] = deepCopy(obj[key])
}
return newObj
}


const set = new Set([1, 2, 3, 4, 5])
const s1 = Symbol("s1")
const s2 = Symbol("s2")
const info = {
name: "jojo",
age: 20,
friend: {
name: "小羊",
age: 19,
address: {
name: "四川",
detail: "成都市"
},
cnt: [1, 2, 3]
},
set: set,
running: function() {
console.log("running")
},
symbolKey: Symbol,

[s1]: "s1",
[s2]: "s2"
}


const newObj = deepCopy(info)

console.log(newObj)

</script>
  • 函数的循环引用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<script>

function isObject(obj) {
const objType = typeof obj
return (obj !== null) && (objType === "object" || objType === "function")
}

// 深拷贝函数
function deepCopy(obj, map = new WeakMap()) {

// symbol类型
if (typeof obj === "symbol") {
return Symbol(obj.description)
}

if(!isObject(obj)) {
return obj
}

// set类型
if(obj instanceof Set) {
const newSet = new Set()
for(const setItem of obj) {
newSet.add(deepCopy(setItem))
}
return newSet
}

// function类型,不需要深拷贝
if(typeof obj === "function") {
return obj
}

// key为symbol类型
const symbolKeys = Object.getOwnPropertySymbols(obj)
for(const symbolKey of symbolKeys) {
obj[symbolKey] = deepCopy(obj[symbolKey], map)
}

// 如果是对象类型
if(map.get(obj)) {
return map.get(obj)
}
const newObj = Array.isArray(obj) ? [] : {}
map.set(obj, newObj)
for (const key in obj) {
newObj[key] = deepCopy(obj[key], map)
}
return newObj
}


const set = new Set([1, 2, 3, 4, 5])
const s1 = Symbol("s1")
const s2 = Symbol("s2")
const info = {
name: "jojo",
age: 20,
friend: {
name: "小羊",
age: 19,
address: {
name: "四川",
detail: "成都市"
},
cnt: [1, 2, 3]
},
set: set,
running: function() {
console.log("running")
},
symbolKey: Symbol,

[s1]: "s1",
[s2]: "s2"
}

info.self = info

const newObj = deepCopy(info)

console.log(newObj)

</script>

事件总线

  • 实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
class yy_EventBus {
constructor() {
// 用于存储所有注册的事件及其对应的回调函数
this.eventMap = {}
}

// 注册事件监听器
on(eventName, eventFn) {
// 获取指定事件名称下的回调函数列表
let eventFns = this.eventMap[eventName]
// 如果列表不存在,则创建一个新的列表
if(!eventFns) {
eventFns = []
this.eventMap[eventName] = eventFns
}
// 将新的回调函数添加到列表中
eventFns.push(eventFn)
}

// 取消事件监听器
off(eventName, eventFn) {
// 获取指定事件名称下的回调函数列表
let eventFns = this.eventMap[eventName]
// 如果列表不存在,则直接返回
if (!eventFns) return
// 遍历列表,找到与传入的 eventFn 相同的函数,并从列表中移除
for(let i = 0; i < eventFns.length; i++) {
const fn = eventFns[i]
if(fn === eventFn) {
eventFns.splice(i, 1)
break
}
}
// 如果列表中已经没有回调函数了,则从 eventMap 中移除该事件
if(eventFns.length === 0) {
delete this.eventMap[eventName]
}
}

// 发布事件,触发指定事件名称下的所有回调函数
emit(eventName, ...args) {
// 获取指定事件名称下的回调函数列表
let eventFns = this.eventMap[eventName]
// 如果列表不存在,则直接返回
if(!eventFns) return
// 遍历列表,依次执行回调函数,并传递参数
eventFns.forEach(fn => {
fn(...args)
})
}
}

// 创建一个事件总线实例
const eventBus = new yy_EventBus()

// 注册事件监听器
eventBus.on("navclick", (name, age, height) => {
console.log(`navclick: ${name}, ${age}, ${height}`)
})

// 另一个事件监听器
const click = () => {
console.log("click")
}
eventBus.on("navclick", click)

// 2 秒后取消 click 监听器
setTimeout(() => {
eventBus.off("navclick", click)
}, 2000)

// 点击按钮时触发事件
const navBtnEl = document.querySelector(".nav-btn")
navBtnEl.onclick = function() {
console.log("navBtnEl clicked")
eventBus.emit("navclick", "张三", 25, 170)
}

JavaScript网络编程

前后端分离的优势

![[Pasted image 20240825203521.png]]

  • 服务器端渲染过程![[Pasted image 20240825203823.png]]
  • 前后端分离渲染过程![[Pasted image 20240825203756.png]]

Http协议

什么是Http

![[Pasted image 20240825204441.png]]![[Pasted image 20240825204459.png]]

网页中资源的获取

![[Pasted image 20240825204658.png]]

Http的组成

![[Pasted image 20240825211123.png]]

Http的版本

![[Pasted image 20240825211713.png]]

Http的请求方式

![[Pasted image 20240825212611.png]]

![[Pasted image 20240826111103.png]]
![[Pasted image 20240826110852.png]]

Response响应状态码

![[Pasted image 20240826120304.png]]

XHR发送请求

XHR发送请求的基本过程

AJAX发送请求

![[Pasted image 20240826150750.png]]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>

// 1.创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()

// 2.监听状态的改变
xhr.onreadystatechange = function() {
// 将字符串转化为json对象
if (xhr.readyState !== XMLHttpRequest.DONE) return
const resJSON = JSON.parse(xhr.response)
const banner = resJSON.data.banner
console.log(banner)
}

// 3.配置请求open
xhr.open("get", "http://123.207.32.32:8000/home/multidata") // 请求方式和请求地址

// 4.发送请求send
xhr.send()

</script>

XMLHttpRequest的state

![[Pasted image 20240826153519.png]]
![[Pasted image 20240826153458.png]]

其他事件监听

![[Pasted image 20240826154018.png]]

响应数据和类型

![[Pasted image 20240826160123.png]]

HTTP响应的状态status

![[Pasted image 20240826160737.png]]

GET/POST请求传递参数

![[Pasted image 20240826212439.png]]![[Pasted image 20240826212419.png]]

ajax网络请求封装

  • 可以使用axios库
  • ajax封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function ajax({
url,
method = "get",
data = {},
success,
failure
} = {}) {
// 1.创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()

// 2.监听数据
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
success && success(xhr.response)
} else {
failure && failure({ status: xhr.status, message: xhr.statusText})
}
}

// 3.设置类型
xhr.responseType = "json"

// 4.配置请求open
if(method.toUpperCase() === "GET") {
const queryStrings = []
for (const key in data) {
queryStrings.push(`${key}=${data[key]}`)
}
url = url + "?" + queryStrings.join("&")
xhr.open(method, url)
xhr.send()
} else {
xhr.open(method, url)
xhr.setRequestHeader("Content-Type", "application/json")
xhr.send(JSON.stringify(data))
}
}

  • ajax-promise封装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function ajax({
url,
method = "get",
data = {},
} = {}) {
return new Promise((resolve, reject) => {
// 1.创建XMLHttpRequest对象
const xhr = new XMLHttpRequest()

// 2.监听数据
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject({ status: xhr.status, message: xhr.statusText})
}
}

// 3.设置类型
xhr.responseType = "json"

// 4.配置请求open
if(method.toUpperCase() === "GET") {
const queryStrings = []
for (const key in data) {
queryStrings.push(`${key}=${data[key]}`)
}
url = url + "?" + queryStrings.join("&")
xhr.open(method, url)
xhr.send()
} else {
xhr.open(method, url)
xhr.setRequestHeader("Content-Type", "application/json")
xhr.send(JSON.stringify(data))
}
})
}

过期时间和取消请求

![[Pasted image 20240827115020.png]]

Fetch和Fetch API

认识Fetch和API

![[Pasted image 20240827133055.png]]![[Pasted image 20240827133115.png]]

Fetch数据的响应

![[Pasted image 20240827133215.png]]
![[Pasted image 20240827134223.png]]
![[Pasted image 20240827134242.png]]
![[Pasted image 20240827134254.png]]
![[Pasted image 20240827134308.png]]

XMLHttpRequest文件上传

![[Pasted image 20240827164139.png]]![[Pasted image 20240827164146.png]]![[Pasted image 20240827164213.png]]

Fetch文件上传

![[Pasted image 20240827164955.png]]