一:面向对象
类,对象,实例三者中对象最大。
一切皆对象,在众多的对象中,发现有一些对象有相同的属性和特征,此时就可以做抽象,最终产出了类。
实例:就是new之后的产物。
1.普通函数和构造函数
function Foo(m, n) {
let ret = m + n
this.m = m
this.n = n
// return ret
return { name: 'liz'}
}
// 01普通函数调用
let ret = Foo(10, 20)
console.log(ret) // 30
// 02构造函数调用
let res= new Foo(10, 20)
console.log(res) // Foo {m: 10, n: 20}
// 03构造函数调用
let res1= new Foo(10, 20)
console.log(res1) // {name: "liz"}
- 不论是当做哪种函数去执行,它终归是一个函数,因此堆栈执行及作用域和作用域链这套内容是存在的。
- 不同的点在于使用 new 关键字的时候会多做一些事情
- 当执行 new 操作的时候浏览器会自动开辟一个内存空间,用于存放实例对象。然后与 this 进行关联
- 如果将一个函数当做构造函数那么默认会返回实例对象,如果说函数内部自己设置了返回值,且这个返回值是引用类型,那么采用自己返回的值。

-
new 操作到底做了什么?
创建对象–>关联this–>返回对象 -
普通函数
- 正常调用,不需要new关键字
- 执行过程还是按着堆栈执行 + 作用域链查找机制
-
构造函数
- 使用new 关键字
- 与普通函数类似,同样会创建私有上下文没然后进栈执行
- 执行new 操作时,浏览器会创建一个空间表示控对象与this进行关联。
- 函数体内如果没有return 或者说return 的是基本数据类型,默认返回对象实例
- 函数体内如果返回引用类型,那么就以自己返回为主
- 函数此时叫做类,返回的结果叫做对象实
-
new 操作符
- 正常情况下使用new完成对象实例创建,如果当前类不需要传递参数,则可以不加括号运行
- new Foo,未加小括号说明Foo不需要传,称之为无参列表
new Foo与new Foo(),的优先级不同,前者为19,后者为20- 每一次new 都会将函数重新执行,生成一个新的执行上下文,创建一个新的实例对象,因此两个实例对象不一样。
2原型及原型链
- 名词:prototype proto Object
- 当前我们只讨论对象(没有顾及函数也是对象这一点)
- 实例对象是对象,所以它的身上会有一个 proto 属性指向当前类的原型( foo1身上的 proto 指向 Foo.prototype)
- 原型对象也是对象,所以它的身上也有一个 proto 属性指向Object 类的原型对象
- ( Foo.prototype.proto ===》Object.prototype )
- 原型链查找规则
- 首先会找自己的私有属性,如果有则直接使用
- 私有属性当中如果不存在,则默认会基于 proto 属性找到它所属类的原型对象
- 如果原型对象身上也没有,则继续基于原型对象身上的 proto 往上接着找,直到找到 Object.prototype 为止
- 如果没有找到就报错
/**
* prototype 属性
* - 每一个函数(除箭头)数据类型,都自带一个 prototype 属性,指向原型对象(Function除外)
* - 每个原型对象都自带一个 constructor 属性,指向当前构造函数本身
* - 函数类数据类型
* + 普通函数、箭头函数、生成器函数
* + 构造函数( 自定义类 )
* + 内置函数(内置构造函数)
*
* __proto__属性
* - 每个对象数据类型,都自带一个 __proto__ 属性(隐式原型)
* - 该属性会指向当前实例所属类的原型对象(prototype)
* - 对象数据类型
* - 普通对象、数组对象、正则
* - prototype 原型对象
* - 实例对象
* - 函数也是对象
*
* Object 类
* - 所有对象如果不知道是 new 谁来的,那么它就是 Object 的一个实例
* - Object 本身也是一个函数,因此同样具有 prototype 属性,指向它自己的原型对象
* - Object原型对象本身也是一个对象,所以它身上也具有一个 __proto__ 属性,内部设计它指向 Null
*
*/
// 下面的Foo是new function来的额
// Foo就是一个对象 他的身上就会有__proto__
// Foo本身是一个函数,他的身上就会有prototype属性
function Foo() {
this.m = 10
this.n = 24
this.getM = function () {
console.log(this.m)
}
}
Foo.prototype.getM = function () {
console.log(this.m)
}
Foo.prototype.getN = function () {
console.log(this.n)
}
// 加小括号的执行优先级会低于不加小括号
// 加不加小括号:如果不需要传递参数,可以不加小括号
let foo1 = new Foo // === new Foo()
let foo2 = new Foo // === new Foo()
// 1.当执行new时,会把Foo函数体内的代码执行一遍
// 2.foo1,foo2当前的两个实例指向的一定是两个内存地址
/*
1.每一个函数都有一个prototype
2.每一个原型对象都有一个constructor属性
3.原型对象必然是一个对象,那么它一定会有一个__proto__属性,这个属性一定指向它所属类的原型对象
*/
console.log(foo1.getM === foo2.getM) // false foo1,foo2当前的两个实例指向的一定是两个内存地址
console.log(foo1.getN === foo2.getN) // true foo1,foo2都不存在getN,去原型对象上找
console.log(foo1.__proto__.getN === Foo.prototype.getN) // true 实例对象是对象,所以它的身上会有一个 __proto__ 属性指向当前类的原型( foo1身上的 __proto__ 指向 Foo.prototype)
console.log(foo1.__proto__.getM === foo2.getM) // false foo1.__proto__.getM找的是原型对象上的getM,foo2.getM是自己的getM
console.log(foo1.getM === Foo.prototype.getM) // false
console.log(foo1.constructor) // Foo构造。 函数foo1自身没有constructor,去原型对象上找。指向构造函数本身。
// ƒ Foo() {
// this.m = 10
// this.n = 24
// this.getM = function () {
// console.log(this.m)
// }
// }
console.log(Foo.prototype.__proto__.constructor) // Object对象 ƒ Object() { [native code] }
// 牵扯到this
foo1.getM() // 10
foo1.__proto__.getM() // undefined
foo2.getN() // 24
Foo.prototype.getN() // undefined


结论
- 堆栈作用域及作用域链(一套机制)
- 原型及原型链(一套机制)
3.Function 与 Object
- 函数
- 普通函数调用(研究:堆栈作用域及作用域链)
- 构造函数( 研究:原型及原型链 )
- 对象( 研究:键值对 ) 【所谓的高阶应用】
- 上述的三种角色之间并没有必然的联系,但是函数是一等公民( 函数就是个函数 )
- 语录
- Function 是一等公民,虽然有很多种身份,但最重要的还是函数
- 每个对象都存在
__proto__属性,指向所属类的原型对象 - 每个函数存在 prototype 属性(有例外),指向它的原型对象【有的函数没有原型对象】
- 所有对象如果不知道谁是爹,那么就都安在 Object 身上 ,且 Object 本身也是一个函数
- Function 与 Object 是JS当中二大并行的基类,虽然最终查找的落脚点都是 Object
- 所有的函数都是 NEW Function 来的
- Function.prototype 原型对象是一个匿名函数,但是它的处理机制和其它的原型地象是一样的(指向Object.prototype)


正常来说,我们遇到的原型对象, 都是一个对象
但是对于 Function来说它的 prototype 是一个匿名函数
虽然它是一个函数,但是它的处理机制和之前的原型对象是一样的,它身上是没有 prototype 属性的
一个函数如果具备了prototype 属性有什么好处,或者说一个函数没有 prototype 属性,它又失去了什么?
不能执行 new 操作
哪些函数不具备 prototype 属性:
- 箭头函数
- Function.prototype 这个函数
- 对象当中的函数简写
Object 类是一个函数,它是 new Function 来的
Function的原型对象又是 new Object.prototype 来的
let obj = {name: 'liz', foo() {}}
不能new obj.foo()
4.this
- this 是什么
this 就是当前函数的执行主体(谁执行了这个函数),不等同于执行上下文,也不等同于作用域。他只是执行上下文中的一个变量。 - 常出现 this 的场景
- 事件绑定
- 普通函数调用
- 构造函数
- 箭头函数(不具备this)
- 基于 call/bind/apply 强制改变 this 的指向
- 规律
- 函数执行的时候需要查看函数的前面是否有 . 如果有,则点前面的对象就执行主体,如果没有 . 一般就是window或undefined
- 特殊情况
- 匿名函数中的 this 是window或者 undefined
- 回调函数中的 this 是window或者 undefined
- 小括号语法
(
function (){
console.log(this) // window
}
)()
let arr= [1,2,3,4,5]
let obj = {name: '拉钩教育'}
arr.map(function (item ,index) {
console.log(this) // window
})
arr.map(function (item ,index) {
console.log(this) // {name: '拉钩教育'}
}, obj)
调用的时候才能确定this指向
let obj = {
fn: function() {console.log(this, 111)}
}
let fn = obj.fn
fn() // 调用的时候才能确定this, window
obj.fn() // obj
// 如果小括号里只有一项,那么和没写小括号是一样的.
(obj.fn)() // 等同于: obj.fn()
// 如果有小括号有多项,那么它会返回最后一项.
// 相当于对最后一次做了一次浅拷贝,下面的代码就好像在执行fn() -> window
(10, fn, obj.fn)() // window
var a = 3;
var obj = { a: 5 }
obj.fn = (function () {
// console.log(this) window,因为是匿名函数和非严格模式。 如果去掉外面的小括号,this就是obj
this.a *= ++a
return function (b) {
// 这里的this是obj
this.a *= (++a) + b
console.log(this.a, a)
}
})()
var fn = obj.fn
obj.fn(6) // this.a = 5 ; a = 13; this.a = this.a * ((++a)* 6) = 95
fn(4) // this.a = 13; a = 13 ; this.a = 13 * (14+4) = 234
console.log(obj.a, a) // 95, 234
/**
* console.log(obj)
* {a: 95, fn: ƒ}
*
*
* console.log(obj.fn, fn)
* ƒ (b) {
this.a *= (++a) + b
}
ƒ (b) {
this.a *= (++a) + b
}
*/

- promise:
- 处理多个promise
1.promise.all等待所有的promise实例都成功,整体状态返回的状态才成功,只要有一个失败,整体失败。
2. promise.rase看多个实例谁先处理完,先处理完成的状态,不论是成功还是失败,就是最后整体的状态。 - promise与微任务
1. then操作
1.当前实例的状态如果是明确的,则会创建一个异步微任务
2.当前实例如果状态是pending,则只是将任务进行保存,并没有创建微任务
2. resolve | reject执行
1.此时会创建一个异步微任务,同步结束后基于状态执行then的相关操作。
- 处理多个promise
案例:
可以看到每次的执行步骤
task queue: 宏任务队列
call stack:执行环境栈
micro queue:微任务执行环境栈
https://www.jsv9000.app/
setTimeout(function foo1() {
console.log('1')
})
Promise.resolve().then(function foo2() {
console.log('2')
}).then(function foo3() {
return Promise.resolve('3').then(function foo4(data) {
setTimeout(function foo5() {
console.log('4')
})
console.log('5')
return data
})
}).then(function foo6(ret) {
console.log(ret)
})
1.foo1会被加入到宏任务.
2.接着执行promise.resolve().then因为promise.resolve()明确知道promise的执行状态是成功,结果是undefined,因此foo2会被立即加入到微任务,形成微任务1。到此,同步代码执行完了一轮。foo3不会被加入到微任务,因为不知道foo2的状态。
3.看微任务队列是否有任务,有一个foo2微任务,拿到执行环境栈中开始执行,输出2,出栈。任务结束。此时foo3的任务状态明确,则foo3加入到微任务队列,同步代码foo6不会被加入到微任务队列,因为foo3的状态不明确。
3.看微任务队列是否有任务,有一个foo3微任务,则开始执行,promise.resolve(‘3’)执行之后状态明确,则.then中的foo4会被加入到微任务队列。接着foo5会被加入到宏任务,形成宏任务2。接着执行console.log('5'),输出5,返回data。同步代码执行完成.
二:事件循环(EventLoop)
2.1EventLoop(事件循环)与EventQueue(事件队列)
Event Loop (事件循环)是一个很重要的概念,指的是计算机系统的一种运行机制。
1.执行顺序
- 浏览器加载界面之后会分配一个线程来执行 JS代码,称之叫 JS引擎(主线程)
- JS引擎会自下而下执行 JS 代码,此过程会遇到(定时器、网络请求、事件绑定、promise)
- 遇到上述的代码之后,浏览器会开启一个 Event Queue(任务|事件)队列 优先级队列结构
- 在队列中存在二个任务队列:微任务micro task | 宏任务 macro task
- 最终会将遇到的异步任务放到 Event Queue队列当中(未执行)
- 主线程会继续向下执行同步代码,直到所有同步代码执行完就会处理异步任务
- 进入 Event Queue 当中查找异步任务,找到之后放入主线程里执行(此时主线程又被步用)
- 执行完一个异步任务之后,主线程再次空闲,此时再进入 Event Queue 查找余下的异步任务
2. 异步编程
2.2 名词说明
JS 当中是否存在异步编程 。是的
JS 是否能同时处理多件事情(JS是多线程的?)。不能同时处理多件事强,因为是单线程。但是js可以实现异步编程。看着好像可以实现同时处理多件事情。
事件循环是为了解决什么问题或者达到了什么效果 。 为了解决阻塞问题,达到了异步编程的效果。
事件循环中的循环是如何体现的。
js是一种机制,是js内部在代码执行的时候,遇到了异步代码之后,他的一种处理方式/手段。为了解决代码的阻塞问题。
2.1.1 进程与线程
- 进程:可以看做是一个应用程序(例如打开浏览器或者浏览器打开一个tab页面,就是来了一个进程)浏览器是多进程的。一个微信就是一个进程。
- 线程:是进程当中具体做事情的人,每个线程同一时刻只能做一件事。
- 一个进程当中可以包含多个线程。
2.1.2 同步编程和异步编程
- 同步编程:一件事一件事的去做,上一件没有完成,下一件不会被处理(单线程)。按代码的顺序去执行。一件事情做完之后才会去另外一件事讲。
- 异步编程:上一件事没有处理完,下一件事可以可以继续去处理(多线程)。并行,串行去执行。
- js代码在浏览器中执行,而浏览器是多进程的,当我们打开一个界面的时候就相当于开启了一个进程。在这个进程中会存在多个线程(js引擎,GUI渲染,Http网络请求, DOM事件监听器,定时器监听线程…)。其中js引擎就来执行js代码,很明显它是一个单线程,因为它是单线程所以默认情况下只能同步完成执行代码,但是我们发现js里也可以实现异步,这是因为它内部存在事件循环和事件队列机制。
- JS是单线程,基于 EventLoop 机制来实现异步编程的。
2.1.3 JS 中的异步操作
- promise(then)
- async/await(generator)
- requestAnimationFrame
- 定时器操作
- ajax(网络请求)
- 事件线绑定
2.1.4 JS 单线程
- 浏览器是多线程的,GUI 渲染线程、JS引擎线程、事件触发线程、异步HTTP请求线程
- 浏览器平台下的 JS 代码是由 JS引擎执行的,所以它是单线程
- JS 中大部分代码都是同步编程,可以基于单线程的 EventLoop 实现异步效果
2.1.5 JS事件循环模型图

EventLoop:是一直轮训的状态。
2.1.6 执行顺序:
- 整体顺序
- 同步任务
- 微任务
- 宏任务
- 异步任务执行顺序
- 先去执行微任务()【如果微任务队列中存在两个微任务,那么先执行哪一个?】先放的先执行(99%都是这样的)。【微任务是否一定先于宏任务执行?不是的。】
- 宏任务(如果有多个宏任务,谁先执行)
总结:不论是微任务还是宏任务,统一定一下,哪个先到执行时机,谁就先执行 (执行时机,从代码中找感觉)
- 定时器
-
setTimeout(function foo1(){}, 1000) 。
问题:
1. 整体是异步任务,还是部分是异步任务? foo1的回调函数是异步的。
2. foo1是什么时候放到异步任务队列中的 ,观点1:1s之后才放入到队列,观点2:setTimeout 是同步操作,立即放入队列?以下围绕:【setTimeout 是同步操作,立即放入队列】展开。 -
定时器执行之后返回一个数值,表示当前系统内的第几个定时器 。
-
将等待时间设置为 0 ,它也不是立即就执行。因为2个方面:1. 定时器做动画是不靠谱的,因为有些时候设置的等待时间已经到了,但是异步操作还没有到执行的时机(微任务)2.每个浏览器都存在一个最小的反应时间( 4~6ms )。
案例1:基本执行顺序:
setTimeout(() => {
console.log(1)
}, 30)
console.log(2)
// 宏任务1,20ms之后执行。
setTimeout(() => {
console.log(3)
}, 20)
console.log(4)
// console.time('AA')
// 下面的循环执行完成需要消耗95ms
for (let i = 0; i < 88888888; i++) { }
// console.timeEnd('AA')
// 这个循环执行之后,宏任务1肯定到时间1。
console.log(5)
// 宏任务2,18ms之后执行。
setTimeout(() => {
console.log(6)
}, 18)
console.log(7)
// 宏任务3,25ms之后执行。
setTimeout(() => {
console.log(8)
}, 25)
console.log(9),
// 1.先执行同步任务,9输出之后说明同步任务执行完了。
// 2.同步任务执行之后,去任务队列,此时没有微任务,所以直接去找宏任务。
// 3.对于宏任务队列来说,谁先到的,谁先执行。但对于当前的代码来说,一定是宏任务1 先到,因为中间有一个95ms的耗时操作(宏任务1,最先放入到任务队列中,放入队列中之后,执行下面的循环花费了95ms,)
// 4.将宏任务1当中的代码拉到主线程中去执行(不考虑执行环境栈),此时主线程是被占用的,没法做其他的事情。
// 5.等到宏任务1执行完之后,会再去找事件队列仍然没有微任务,接着看宏任务,这次发现宏任务2到了
// 。。。。。。
// 执行结果
// 2
// 4
// 5
// 7
// 9
// 3 3比1先到执行时机,所以3先输出。1肯定是先放进去的,但是3是20ms,3是30ms,3比1先到执行时机,因此3先输出
// 1
// 6
// 8

案例 2: 主线程被占用:
// 宏任务1:
setTimeout(() => {
console.log(1)
}, 0)
console.log(2)
// while (true) { } // 这里有一个死循环,意味着主线程一直被占用( JS单线程,分不出来身去做其它的事情了 )
// throw new Error('手动抛出异常') // 代码执行时如果有异常,它不会影响异常出现之前所存放的异步操作,但是异常之后不能执行了
// console.log(a) // 代码执行时如果有异常,它不会影响异常出现之前所存放的异步操作,但是异常之后不能执行了
console.log(3)
// 宏任务2:
setTimeout(() => {
console.log(4)
}, 10)
console.log(5)
/*
1.执行结果 2, 3, 5, 1, 4
2.开始while (true) { }
执行结果:2 。因为:这里有一个死循环,意味着主线程一直被占用( JS单线程,分不出来身去做其它的事情了 )
3.关闭:while (true) { },开启: throw new Error('手动抛出异常')。
执行结果: 2 , 报错:‘手动抛出异常’, 1。
因为:代码执行时如果有异常,它不会影响异常出现之前所存放的异步操作,但是异常之后不能执行了。
4. 关闭:while (true) { }, throw new Error('手动抛出异常')。开启:console.log(a)
执行结果:2, a is not defined, 1 . 可以发现手动抛出异常,和代码异常执行结果一样,只是报错信息不一样。
*/
案例3: promise语法分析:

// 1.只有一行代码:执行结果:报错。
const p1 = new Promise()
// 2.下面的执行结果:报错
const p1 = new Promise({ obj: '123' })
// 3. 执行下面的p1:会有结果输出么?有:输出结果:111。
// executor 里虽然能写同步的代码但是,我们一般用它来管理异步代码。
const p1 = new Promise((resolve, reject) => {
console.log('111')
})
// 4.下面的执行结果:ok。 下面的ok会不会输出?会输出的, 1ms之后会输出的。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('ok')
}, 1000)
})
// 4.promise 的状态一旦切换了,就不会再改变。
// 下面的代码执行结果:成功了:,ok。
const p1 = new Promise((resolve, reject) => {
resolve('ok')
reject('no')
})
console.log(p1)
p1.then((ret) => {
console.log('成功了:', ret)
}, (reason) => {
console.log('失败了', reason)
})
/*
promise:
1:executor 函数
1. 我们在执行 new Promise 操作的时候必须传入一个参数,且这个参数只能是函数, 这个函数我们称之为 executor 函数。executor是同步行为,立即执行。
2. executor 函数能接收二个函数做为参数, 且 executor 函数是立即执行了。
3. executor 里虽然能写同步的代码但是,我们一般用它来管理异步代码。
2. Promise实例
1. new 操作执行之后肯定会返回一个值,这个值就是 Promise 实例
2. [[PromiseState]]: Promise 状态, pending(默认状态) fulfilled(成功) rejected(失败)
3. [[PromiseResult]]: Promise 值, undefined 成功的值 失败的原因
4. __proto__ 属性可以找到原型对象,身上有 then catch finally
3. 状态切换
1. 执行 resolve 的时候将实例状态改变为成功态,此时值就是当前 resolve 调用时接收的值。
2. 执行 reject 的时候将实例状态改变失败态,此时的值就是.....
3. 如果executor 函数执行报错,状态非常明确:是失败态。则状态也会切换至失败态,此时promise 的值就是当前报错的原因。
4. promise 的状态一旦切换了,就不会再改变( )
5. 关注promise的状态:决定了:要不要立即把这个微任务放到任务队列当中。
6. 关注promise的值:会帮助你分析代码的最终输出结果是什么。
4. 状态和值
获取:获取实例对象 的2种方法:
1. new Promise 获取实例对象。
1. 调用 resolve reject
2. executor 函数执行中是否存在报错
2. 调用 then 方法返回实例对象。
1. then 注入的两个主法不论哪个执行,只要执行不报错, 则新实例的状态就是 fulfilled , 反之就是 rejected ,值就是返回值
2. 手动返回一个 promise ,此时新 promise 的值和状态取决当前返回的 promise
*/
案例分析4:执行顺序总结:
let p1 = new Promise((resolve, reject) => {
console.log(1)
// resolve('ok')
// console.log('2')
// 下面的setTimeout函数里面的代码:按照同步的逻辑执行
setTimeout(() => {
resolve('ok')
console.log('2')
}, 1000)
})
// then何时加入到微任务队列中,取决于p1的状态,只有p1的状态明确(成功 / 失败),才会把then放入到微任务队列中。
// 当setTimeout中的resolve('ok')执行完之后,才会知道p1的状态,是成功的。因此此时,p1才会加入微任务队列中。
// 此时setTimeout函数中的代码在主线程中执行着,因此输出2。输出之后,主线程的代码执行完毕。
// 然后去微任务队列中,看是否有任务,有一个then任务,然后拉到主线程当中执行,输出了 success
p1.then((ret) => {
console.log('success--->', ret)
}, (reason) => {
console.log('failed--->', reason)
})
console.log(3)
// 执行结果:1 -> 3 -> 2 -> success:ok
/**
* 分析:
* 01 executor 函数是立即执行的,所以 1 输出了。
* 02 在 executor 函数当中存在 setTimeout ,这时就会在事件列队中会加一个宏任务1,此任务在 1000ms 后执行
* 03 p1.then 开始执行(注册了二个任务)pending:不是一个明确的状态。
* 此时p1的状态是不明确的,那么 then 后面的微任务还没有添加到的队列当中)
* 如果你此处认为将微任务添加到队列当中了,那么请记着,这个微任务的执行时机一定是等到 p1 状态明确之后才会执行
* p1的状态明确就需要等到 setTimeout 执行完,请思考 setTimeout 是一个什么任务,then 又是一个什么任务。setTimeout是宏任务,then是微任务。先执行setTimeout,再执行then。
* 所以我们发现不是说 所有的微任务都一定先于宏任务执行。
* 04 输入了 3 ,当这行代码执行完成之后,就相当于所有的同步代码执行完了。
* 05 找异步任务,首先换微任务, 没有的时候就找宏任务, 所以此时会调用 resolve('ok')
* 06 当我们去调用 Ok 的时候就会明确 p1 的状态,那么就会将 then 的回调添加到微任务队列当中,但是 setTimeout 里的代码
* 现在是在主线程当中运行着,因此 2 肯定先输出,当2 输出之后就意味着这次的同步执行完了,那么再继续去队列当中查找
* 07 这次查找就看到了微任务,然后拉到主线程当中执行,输出了 success
* 1 3 2 success
*
* 微任务先执行,然后再找宏任务( 第一轮,第二轮.......在一次循环当中,微先于宏 )
*
*
*/
案例:
const p1 = new Promise((resolve, reject) => {
resolve('ok')
})
let p2 = p1.then((result )=>{
console.log('success:', result)
setTimeout(()=>{
console.log('okk')
}, 1000)
}, reason => {
console.log('faild:', reason)
return 20
})
p2.then((result)=>{
console.log('success:', result)
}, reason => {
console.log('faild:', reason)
})
// 执行结果:
// success: ok
// success: undefined
// promise实例
// okk
- 分析async的特点和 await特点:
// 1.下面的函数输出结果:返回promise实例,状态是:成功;值:100。
// 因为:async 用于修饰函数,默认让返回返回一个成功的 promise 实例
async function foo() {
return 100
}
console.log(foo())
// 2.立即执行foo函数,如果foo的返回结果是promise实例,则直接返回,如果不是promise实例,处理成promise实例再返回。
// 返回之后,不管后面的代码有多少,把后面的所有代码整体看成是一个微任务。
await foo()
console.log(111)
/**
* 1. async 特点
1. async 用于修饰函数,默认让返回返回一个成功的 promise 实例
2. 如果函数执行报错,则promise 状态为 rejected ,值就是报错原因
3.如果函数执行正常则实例对象的状态为 fulfilled, 值为函数的返回值,如果没有return 则 undefined
2. await 语法 (基于async)
1. await 后面放置一般都是 promise 实例,如果不是,它会给你处理成 Promise.resolve()
2. await foo(), 此语法就相当于是立即执行 foo函数, 接收到 foo 函数的返回值之后处理为 Promise
3. 假如说 await foo() 后面还有代码,它就会将后面的代码整体看做是一个异步的微任务,等到它前面的 promise 状态明确之后再按规则执行。
*/
案例:
// 案例:
function foo(){
console.log(1)
return new Promise((resolve, reject)=>{
// 宏任务1,1000ms之后执行
setTimeout(()=>{
resolve(2)
}, 1000)
})
}
// 第一步1.同步任务,输出1
console.log(3)
async function fn() {
console.log(4)
let result = await foo()
//微任务1: await 后面的不管多少行代码被整体看成微任务:他的代码执行时机:一定是等到上面的await 后面的函数状态明确之后才会执行。await后面的foo返回promise实例。
console.log(result)
console.log(5)
}
// 第2步: 调用fn函数
fn()
// 第3步:同步输出6。输出6之后,同步代码执行完成。
console.log(6)
/*
分析:
1.console.log(3) 同步任务,输出1.
2.调用fn函数,输出4, 输出之后遇到await,立即执行,则输出1,接着执行foo函数中的return new Promise,添加了一个宏任务,1000ms之后执行,执行之后返回的状态不明确。
console.log(result) console.log(5)整体被看成是一个:微任务,这个微任务的执行时机,一定是等到上面的promise状态明确之后再执行。
3.继续执行同步任务,输出6。
4.输出6之后,发现同步任务执行完成。(同步任务执行完之后,开始找微任务,但是发现微任务的执行时机取决于上面的promise状态明确,因此宏任务1开始执行。)
5.然后开始到任务队列中找宏任务1,宏任务1调用之后就明确了状态(resolve成功状态),且将2向后传递。
6.开始执行微任务1,console.log(result) console.log(5)。认别输出: 2, 5
*/
// 执行结果:
// 3
// 4
// 1
// 6
// 2
// 5
本文详细解析了JavaScript中的面向对象原理,类与对象的关系,构造函数与普通函数的区别,以及new操作的深入剖析。同时介绍了堆栈与作用域链、原型链的概念,以及Function与Object的角色。核心内容涵盖了异步编程中的EventLoop、EventQueue和Promise机制,通过实例和案例探讨了事件循环的工作原理和执行顺序。

2312

被折叠的 条评论
为什么被折叠?



