一、 异步 和 单线程
- 同步:必须等待上一步执行完毕后,下一步才可以执行
- 异步:在等待上一步执行的同时,可以进行其他处理
- 进程:程序的一次执行,它占有一片独有的内存空间
- 线程:CPU的基本调度单位,是程序执行的一个完整流程
- JS是单线程的
1. 同步任务和异步任务
1. 同步任务
非耗时任务
,指主线程上排队执行的任务- 同步会阻塞代码执行
- 只有前一个同步任务执行完成,才能进行下一个同步任务
- 构造函数属于同步任务
2. 异步任务 耗时任务
,指由js委托给**宿主环境
**(js的执行环境,如浏览器、node.js等)进行执行- 异步不会阻塞代码
- 当异步任务执行完成后,会通知JavaScript主线程执行异步任务的**
回调函数 callback
**
2.宏任务和微任务
异步任务
分为宏任务和微任务
2.1 宏任务
- 异步Ajax请求
- setTimeout、setInterval
- DOM 事件监听
- 文件操作
- 其他宏任务
宏队列
:用来存放宏任务的容器
2.2 微任务
- Promise.then、.catch、.finally
- process.nextTick
- async、await
- 其它微任务
微队列
:用来存放微任务的容器
2.3 执行顺序
- 页面第一次渲染:初始化同步代码 ==> 所有的微任务 ==> 渲染界面 ==> 执行第一个宏任务 ==> 所有的微任务 ==> 渲染界面 ==> 执行第一个宏任务
- 界面更新渲染:所有的微任务 ==> 界面渲染 ==> 第一个宏任务
3. 事件轮询机制(event loop,异步实现的原理)
- JS是通过事件轮询机制来实现单线程异步的
二、Promise
1. 认识 Promise
- Promise是一种异步操作的解决方法
- Promise之前解决异步的常用方式:回调函数
- Promise 可以解决回调地狱问题(可阅读性差,任然需要回调,但是不会嵌套)
- Promise指定回调的时机更加灵活(可以指定在异步操作启动后或完成后)
2. Promise 的基本用法
2.1 实例化构造函数生成实例对象
- 在 new 的时候必须指定回调函数,否则会报错
- 该回调函数接收两个参数:resolve、reject,这两个参数也是回调函数,resolve是成功时的回调,reject是是失败的回调
const p = new Promise((resolve,reject) => {
// 回调函数
})
- promise中可以不写具体逻辑,只需要写明何时调用resolve 或 reject
2.2 promise 的状态
- pending:进行中
- fulfilled / resolved:成功
- rejected:失败
状态的改变不可逆,只能从进行中变为成功或失败
- 不调用resolve或reject时,是pending状态
const p = new Promise((resolve,reject) => {
// 回调函数
})
console.log(p)
- 调用resolve()时,会由pending 变为 fulfilled
const p = new Promise((resolve,reject) => {
// 回调函数
resolve()
})
console.log(p)
- 调用reject时,会由 pending 变为 rejected
const p = new Promise((resolve,reject) => {
// 回调函数
reject()
})
console.log(p)
2.3 then方法
- then方法有两个回调函数,一个成功、一个失败
p.then(() => {
// 成功的回调
console.log('成功')
},() => {
// 失败的回调
console.log(('失败'))
})
2.4 resolve 和 reject 函数的参数
- 在创建 Promise调用resolve或reject的时候,可以进行传参
- 传递参数在then对应的回调中可以接收
const p = new Promise((resolve,reject) => {
// 失败
reject(new Error('falied'))
})
p.then(() => {
// 成功的回调
console.log('成功')
},(err) => {
// 失败的回调
console.log(err)
})
3. Promise 的实例方法
3.1 then()
- Promise状态由 pending ==》 fulfilled 时,执行then的第一个回调函数(成功的回调)
- Promise状态由 pending ==》 rejected时,执行then的第二个回调函数(失败的回调)
-
then 的回调函数中,return的是Promise包装后的
-
then 方法执行后会返回一个
新的 Promise 对象
-
该新 Promise 对象的状态是由 then 方法指定,如果不指定默认是 fulfilled(成功状态)
-
如果要指定成功的参数,直接写在 then方法回调函数的return后即可
-
如果要指定失败的参数,可以在return后面新建一个 Promise
const p = new Promise((resolve,reject) => {
// 成功
resolve('调用成功!!')
// 失败
// reject(new Error('falied'))
})
p.then((data) => {
// 成功的回调
console.log(data)
// 返回成功的Promise
// return 'then方法成功的返回'
// 返回失败的Promise
return new Promise((resolve,reject) => {
reject('then方法中返回失败的回调')
})
},(err) => {
// 失败的回调
console.log(err)
}).then((data) => {
console.log(data);
},(err) => {
console.log(err);
})
3.2 catch()
- then 既可以处理成功,也可以处理失败状态
- 一般开发中,then 用于处理成功的状态
- catch 用于处理失败的状态
new Promise((resolve,reject) => {
// resolve('成功')
reject('失败')
}).then(data => {
console.log(data);
}).catch(err => {
console.log(err);
})
- catch 可以捕获它前面的错误
- 一般建议在Promise对象后跟一个catch方法,用于捕获Promise的内部错误
3.3 finally()
- 当Promise状态发生变化,不管什么变化都会执行 finally
3. Promise 的构造函数方法
3.1 Promise.resolve() 和 Promise.reject()
3.1.1 Promise.resolve()
- Promise.resolve() 是成功状态 Promise 的简写
// 完整写法
new Promise(resolve => {
resolve()
})
// 简写
Promise.resolve()
- Promise.resolve() 的参数
- 一般参数
- 参数为 Promise对象
- 当参数为Promise对象时,直接返回这个Promise,不会进行其他处理
- 当resolve 接收的是Promise对象时,后面的then会根据传递的Promise对象的状态变化决定执行哪一个回调
- 参数为:具有 then 方法的对象
3.1.2 Promise.reject()
- 失败状态的简写
- Promise.reject()不管哪种参数,都会原封不动向后传递,作为后续方法的参数
3.2 Promise.all()
Promise.all ([ Promise1,Promise2,… ])
- 关注多个Promise 对象的状态变化
- 传入多个 Promise 实例,然后包装成一个新的 Promise实例进行返回
- 只有需要包装的多个Promise实例状态都为成功,新的Promise状态才为成功
(等待机制)
3.3 Promise.race()
Promise.race([promise1,promise2,…])
- 关注多个Promise 对象的状态变化
- 传入多个 Promise 实例,然后包装成一个新的 Promise实例进行返回
- 新 Promise的状态取决于第一个完成的Promise对象,如果第一个完成的成功了,那最终就成功;如果第一个成功的状态是失败,那最终的就是失败
- 只要其中任何一个异步完成,就会立即执行下一步的.then()操作(
赛跑机制)
3.4 Promise.allSettled()
- 该方法的状态与 Promise 状态无关,永远都是成功的
- 它只会记录下各个 Promise的表现
4. Promise 的注意事项
- 推荐在调用 resolve 或 reject 的时候加上return,避免再去执行其后的代码
- Promise.all / race / allSettled 的参数如果不是数组形式,会被自动转为数组
- Promise.all / race / allSettled 的参数是任何 可遍历的
5. Promise应用 —— 异步加载图片
const loadImgAsync = url => {
return new Promise((resolve,reject) => {
const img = new Image()
// 图片加载成功
img.onload = () => {
resolve(img)
}
// 图片加载失败
img.onerror = () => {
reject(new Error(`Could not load image at ${url}`))
}
// 加载图片
img.src = url
})
}
// 获取图片的DOM元素
const imgDOM = document.getElementById('img')
loadImgAsync('http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/dfc11bb0-4af5-4e9b-9458-99f615cc685a.jpg').then(img => {
// 替换原来的src
imgDOM.src = img.src
}).catch(err => {
console.log(err)
})
二、 Class
1. 初识 class
1.1 class是什么
语法:
class 类名 {
// 构造方法
constructor(){}
}
- 类名的首字母一般大写
- 实例化类的时候会执行
构造方法
(constructor),可以不写构造方法(浏览器会自动补全) - 构造方法中一般是类的属性,方法一般不写在构造函数中
- 类中的公用方法,一般定义在构造方法外部,语法:
方法名() {}
- 公用方法中要使用构造方法中的属性时:
this.属性名
- 类中方法与方法之间没有逗号分隔
- 类只能通过 new 来实例化
class Person {
constructor(name,age) {
this.name = name
this.age = age
}
// 公用的方法
sayHi() {
console.log(`大家好!我是${this.name},今年${this.age}岁!`)
}
}
// 实例化类
const dudu = new Person('嘟嘟',18)
dudu.sayHi()
1.2 class 和 构造函数
构造函数 | Class |
---|---|
function 函数名(属性1,…) { } | class 类名 { constructor(属性1,…){ } } |
公用方法一般写在原型上: 函数名.prototype.公用方法名 | 公用方法和构造方法constructor平级:公用方法名() {} |
- Class 的本质也是函数
1.3 Class的定义
1.3.1 声明式的定义
class 类名 {
// 构造方法
constructor(){}
}
1.3.2 表达式的定义
const 类名 = class {
constructor() {}
}
2. Class 的属性和方法
2.1 实例属性
- 构造方法(constructor )中的属性就是实例属性
- 构造方法中的 this 指向实例对象
- 一般会在类中,构造方法constructor 前,列出所有的实例属性并设置默认值,这样便于观察
2.2 静态方法
- 静态方法就是类的方法
- 之前和constructor 平级写的公用方法属于实例方法,实例方法需要实例化类后才可以调用
- 静态方法不需要实例化就可以调用
- 语法 :
static 方法名 () {}
,调用:类名.方法名()
- 实例方法中的this指向实例对象
- 静态方法指向的是类本身,而不是实例对象
2.3 静态属性
- 静态属性即类的属性
- 语法:类的外部
类名.属性名 = 属性值
(不推荐) - 可以将静态属性改写静态方法的形式,最终返回需要的属性值
- 静态属性的调用 :
类名.属性名
2.4 私有属性和方法
- 只能在类的内部使用的属性和方法
- 一般情况下,类的属性和方法都是公开的
- 目前没有对应的私有属性和方法的语法,但可以模拟私有属性和方法
- _ 开头表示私有
- 将私有的属性和方法从类中移出
3. Class 的继承
3.1 子类继承父类 extends
- 子类可以通过
extends
关键字实现对父类的继承 - 类都有构造方法,在子类的构造方法中需要调用父类的构造方法:
super()
- 子类会继承父类的
全部属性和方法
// 父类
class Person {
// 构造方法
constructor(name,age) {
this.name = name
this.age = age
// 每创建一个实例就会创建一个该方法,不推荐这种写法,容易造成浪费
this.sport = function() {
console.log(`${name}经常运动~~~`)
}
}
// 公用的方法
sayHi() {
console.log(`大家好!我是${this.name},今年${this.age}岁!`)
}
// 静态方法
static study() {
console.log(`${name}喜欢学习!`)
}
}
// 父类的静态属性
Person.nationality = '中国'
// 子类
class Programmer extends Person {
constructor(name,age) {
// 调用父类的构造方法
super(name,age)
}
}
// 实例化子类
const dudu = new Programmer('嘟嘟',24)
dudu.sayHi()
dudu.sport()
Programmer.study()
console.log(Programmer.nationality)
- 子类可以改写继承的属性和方法
- 子类还可以定义自己
特有的属性和方法
- 同名覆盖原则,子类的属性和方法和父类同名,则子类的会覆盖父类的
- 子类如果要添加自己的属性,则要写在
super 后面!!!
3.2 super
- super既可以作为函数调用,也可以作为对象使用
3.2.1 super作为函数调用
- 代表调用父类的构造方法,只能用在子类的构造方法中,用在其他地方会报错
- 但此时 super 中的 this 指向子类的实例对象
3.2.2 super 作为对象使用
- 在构造方法或一般方法中使用
- 代表父类原型对象,故定义在父类实例上的属性或方法,无法通过super调用
- 通过 super 调用父类的方法时,方法内部的 this 指向当前子类的实例对象
- 在静态方法中使用
- 代表父类,而不是父类的原型对象
- 通过 super 调用父类方法时,方法内部的 this 指向当前的子类,而不是子类的实例对象
3.2.3 注意事项
- 使用 super 时,必须显示指定是作为函数还是对象使用