JS 高级核心概念
事件循环原理
提示:
JavaScript作为一门单线程语言,必须要理解和掌握事件循环(Event Loop)和任务队列(Task Queue)的机制,高效地处理异步任务,保证用户体验的流畅性。
事件循环概述
说明:
在事件循环中,当主线程执行完当前的同步任务后,会检查事件队列中是否有待处理的事件。如果有,主线程会取出事件并执行对应的回调函数。这个循环的过程被称为事件循环(Event Loop),它由主线程和任务队列两部分组成。主线程负责执行同步任务,而异步任务则通过任务队列进行处理。这种机制保证了异步任务在适当的时机能够插入执行,从而实现了JavaScript的
非阻塞异步执行
。
事件循环流程如下:
- 主线程读取JavaScript代码,形成相应的堆和执行栈。
- 当主线程遇到异步任务时,将其委托给对应的异步进程(如Web API)处理。
- 异步任务完成后,将相应的回调函数推入任务队列。
- 主线程执行完同步任务后,检查任务队列,如果有任务,则按照先进先出的原则将任务推入主线程执行。
- 重复执行以上步骤,形成事件循环。
同步任务
说明:
同步任务是按照代码的书写顺序一步一步执行的任务。当主线程执行同步任务时,会阻塞后续的代码执行,直到当前任务执行完成。典型的同步任务包括函数调用、变量赋值、算术运算等。例如:
console.log('Step 1');
let result = add(2, 3);
console.log(result);
console.log('Step 2');
function add(a, b) {
return a + b;
}
在上面的例子中,console.log('Step 1')
执行完毕后才会执行函数调用 add(2, 3)
,并等待 add
函数返回结果后才会继续执行后续代码。
异步任务
说明:
异步任务是在主线程执行的同时,通过回调函数或其他机制委托给其他线程或事件来处理的任务。在执行异步任务时,主线程不会等待任务完成,而是继续执行后续代码。包括:
- 回调函数
callback
Promise/async await
Generator
- 事件监听
- 发布/订阅
- 计时器
requestAnimationFrame
MutationObserver
process.nextTick
I/O
操作
不得不说,异步执行的机制使得 JavaScript 能够更好地处理耗时操作,保持页面的响应性。
在上述例子中,setTimeout
是一个异步任务,它会在1秒后将回调函数推入任务队列,而主线程不会等待这个1秒,而是继续执行后面的 console.log('End')
。当主线程的同步任务执行完成后,它会检查任务队列,将异步任务的回调函数推入执行栈,最终输出 'Timeout callback'
。
任务队列类型
- 任务队列
上面我们讨论了同步任务和异步任务的执行过程,接下来我们将进一步探讨任务队列,了解它的最小颗粒度是如何执行的。
任务队列分为宏任务队列
(macrotask queue)和微任务队列
(microtask queue)两种。JavaScript 引擎遵循事件循环的机制,在执行完当前宏任务后,会检查微任务队列,执行其中的微任务,然后再取下一个宏任务执行。这个过程不断循环,形成事件循环。
1、宏任务(Macrotasks)是一些较大粒度的任务,包括:
- 所有同步任务
- I/O操作,如文件读写、数据库数据读写等
- setTimeout、setInterval
- setImmediate(Node.js环境)
- requestAnimationFrame
- 事件监听回调函数等
- ...
2、微任务(Microtasks)是一些较小粒度、高优先级的任务,包括:
- Promise的then、catch、finally
- async/await中的代码
- Generator函数
- MutationObserver
- process.nextTick(Node.js 环境)
- ...
任务执行过程
首先,必须要明确,在JavaScript中,所有任务都在主线程上执行。任务执行过程分为同步任务和异步任务两个阶段。异步任务的处理经历两个主要阶段:Event Table
(事件表)和 Event Queue
(事件队列)。
Event Table
存储了宏任务的相关信息,包括事件监听和相应的回调函数。当特定类型的事件发生时,对应的回调函数被添加到事件队列中,等待执行。例如,你可以通过addEventListener
来将事件监听器注册到事件表上:
document.addEventListener('click', function() {
console.log('Hello world!');
});
微任务与 Event Queue 密切相关。当执行栈中的代码执行完毕后,JavaScript引擎会不断地检查事件队列。如果队列不为空,就将队列中的事件一个个取出,并执行相应的回调函数。
任务队列的执行流程可概括为:
- 同步任务在主线程排队执行,异步任务在事件队列排队等待进入主线程执行。
- 遇到宏任务则推进宏任务队列,遇到微任务则推进微任务队列。
- 执行宏任务,执行完毕后检查当前层的微任务并执行。
- 继续执行下一个宏任务,执行对应层次的微任务,直至全部执行完毕。
- 这个流程确保了异步任务能够在适当的时机插入执行,保持程序的高效性和响应性。
这个流程确保了异步任务能够在适当的时机插入执行,保持程序的高效性和响应性。
如果看到这里,还觉得有点懵,我们不妨看看下面这个示例解析,一定会让你清晰明了!!!
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
new Promise((resolve) => {
console.log(4);
resolve();
console.log(5);
}).then(() => {
console.log(6);
});
console.log(7);
执行顺序解析:1 => 3 => 4 => 5 => 7 => 6 => 2。
- 创建
Promise
实例是同步的,所以1、3、4、5、7是同步执行的。 then
方法是微任务,放入微任务队列中,在当前脚本执行完毕后立即发生。- 同步任务执行完毕后,执行微任务队列中的微任务。
- 最后,
setTimeout
放入宏任务队列,按照先进先出的原则执行。
注意:出现
async
、await
,等价于promise
、then
。
- 再来一题
搞懂这个说明你没有问题了,
事件循环并不是很难,但很重要
。
详情:
console.log(1)
console.log(2)
setTimeout(() => {
console.log(3)
}, 1000)
new Promise(r => {
console.log(4)
r(5)
console.log(6)
}).then((value) => {
console.log(value)
console.log(7)
})
new Promise((resolve, reject) => {
console.log(8)
resolve(9)
console.log(10)
}).then((value) => {
console.log(value)
console.log(11)
})
function greet () {
setTimeout(() => {
new Promise((resolve, reject) => {
reject('error')
resolve(12)
console.log(13)
}).then((value) => {
console.log(value)
}, (reason) => {
console.log(reason)
})
})
console.log(14)
}
greet()
console.log(15)
// 宏任务排队: 3↓ 13 promise↓: error
// 微任务排队: 5 7 9 11
// 结果
// 1 2 4 6 8 10 14 15 ---> 5 7 9 11 3 13 error (第一个 timeout 没有加延时时间的结果, 没有加延时,默认按照宏任务代码书写的先后顺序执行)
// 1 2 4 6 8 10 14 15 ---> 5 7 9 11 13 error 3 (第一个timeout加了延时的结果,宏任务按照时间的时间优先级执行)
- 最后一题
用
浏览器
运行看看结果验证你的结果
详情:
console.log(1)
console.log(2)
setTimeout(() => {
console.log(3)
}, 1000)
new Promise(r => {
console.log(4)
r(5)
console.log(6)
}).then((value) => {
console.log(value)
console.log(7)
})
new Promise((resolve, reject) => {
console.log(8)
resolve(9)
console.log(10)
}).then((value) => {
console.log(value)
console.log(11)
})
async function greet () {
setTimeout(() => {
new Promise((resolve, reject) => {
reject('error')
resolve(12)
console.log(13)
}).then((value) => {
console.log(value)
}, (reason) => {
console.log(reason)
})
})
console.log(14)
return 20
}
greet().then(value => {
console.log(value)
})
console.log(15)
/**先执行同步任务 -> 微任务 -> 宏任务 */
this 指向与绑定
指向问题
- 全局环境下的 this 指向 window
console.log(this); // window
- 函数独立调用,函数内部的 this 也指向了 window
const fn = () => {
console.log(this);
};
fn(); // window
function func() {
console.log(this);
}
func(); // window
- 被嵌套的函数独立调用时,this 默认指向了 window
const obj = {
a: 10,
fn() {
const $this = this;
function fn1() {
/**这是一个嵌套函数 */
console.log(this); // window
console.log($this); // obj 对象
}
fn1(); // 独立调用
},
};
obj.fn();
- IIFE 自执行函数
var num = 10;
function fn() {
(function () {
console.log(this.num);
})();
}
const obj = {
num: 1,
foo: fn,
};
obj.foo(); // 10
绑定问题
详解:
推荐:前往 bilibili
- 如何控制 this ?
函数的 this 由运行时函数的调用者决定,因此我们需要对this指向不明确时,使用
将 this 存储在变量身上
、bind
、call
、apply
等方式来解决this
的指向问题,以便我们能够对this
进行控制和使用。
1、利用变量的方式存储 this
const XiaoMing = {
name: "小明",
sayHi() {
const $this = this;
setTimeout(function () {
// console.log(`Hi, my name is ${this.name}`); // 此时就不知道 this 指向谁了
console.log(`Hi, my name is ${$this.name}`); // 将 this 存储在 $this 变量身上
}, 1000);
},
};
console.log(XiaoMing);
XiaoMing.sayHi();
2、利用箭头函数
由于箭头函数中没有
this
,所以就会向外寻找this
,从而实现在回调函数中使用this
能够指向我们想要指向的对象
const XiaoMing = {
name: "小明",
sayHi() {
setTimeout(() => {
// console.log(`Hi, my name is ${this.name}`); // 此时就不知道 this 指向谁了
console.log(`Hi, my name is ${this.name}`); // 将 this 存储在 $this 变量身上
}, 1000);
},
};
console.log(XiaoMing);
XiaoMing.sayHi();
3、利用 bind 进行绑定函数的 this
利用函数原型上的
bind
方法可以绑定函数中的this
指向,该方法会返回一个原函数的副本函数,并不会修改原函数的任何内容,只对this
进行了绑定。
bind
函数原型:Function.bind(this: Function, thisArg: any, ...argArray: any[]): any
bind
第一个参数是要指定的this
绑定对象,其他参数以剩余参数的方式传入,内部会收集起来继续传递给原函数作为原函数的参数。
function greet() {
console.log("this: ", this);
}
let jack = {
name: "jack",
age: 20,
};
greet(); // this指向环境中的全局对象
let newFunc = greet.bind(jack); // bind 会返回一个新的函数
newFunc(); // 该函数中的this变成了指定的对象
4、利用 call/apply
来改变 this
的指向
通过调用
call
方法告诉 js 引擎,我要调用这个函数,并且告诉引擎this
指向谁
function greet(name = "default") {
this.name = name;
console.log(`hello my name is ${this.name}`);
}
let jack = {
name: "jack",
};
greet.call(); // 如果不传,则表示没有人调用,那么 js 引擎会解析为默认的函数原本的调用方式
greet.call(jack, "jack"); // 传入的第一个参数是 this 指向的对象,后面的参数是原函数的参数,以参数列表的形式传入,内部会收集起来作为参数传递给原函数
greet.apply(jack, ["jack"]); // 传入的参数是原函数的参数,以数组的形式传入,内部会收集起来作为参数传递给原函数
call
方法的原型Function.call(this: Function, thisArg: any, ...argArray: any[]): any
- 与
apply
方法的原型Function.apply(this: Function, thisArg: any, argArray?: any): any
区别:
call
方法使用剩余参数的方式
将传入的一个个的散开的参数收集到列表中:传入需要传递给原函数的多个参数
apply
方法没有做收集剩余参数的语法,而是使用接收数组的方式来接收需要传递给原函数的多个参数:参数以数组的方式包装传递
深入学习 Promise
Promise 是什么?
- 抽象表达
- ES6 规范
- JS进行异步变成的新解决方案(旧方案是回调函数)
- 具体表达
- 从语法上来说:Promise是一个构造函数
- 从功能上来说:Promise对象用来封装一个一步操作并可以获取其成功或失败的结果值
异步编程:
- fs文件操作
- 数据库操作
- ajax
- 定时器
- ...
为什么要用Promise?
- 指定回调函数的方式更加灵活
- 支持链式调用,可以解决回调地域问题
Promise的状态
- pending (待定状态)
- pending 变为
fulfilled
- pending 变为
rejected
说明:只有这三种,且一个 promise 对象只能改变一次,无论变为成功还是失败,都会有一个结果数据,成功的结果数据一般称为
value
,失败的结果数据一般称为reason
。
ajax 案例
详情:
const btn = document.getElementById('btn')
btn.addEventListener('click', () => {
const p = new Promise((resolve, reject) => {
// 1.创建对象
const xhr = new XMLHttpRequest()
// 2.初始化
xhr.open('GET', 'https://api.apiopen.top/getJoke')
// 3.发起请求
xhr.send()
// 4.监听响应事件
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 判断响应状态码
if (xhr.status >= 200 && xhr.status < 300) {
// 控制台输出响应体
// console.log(xhr.response)
resolve(xhr.response)
} else {
// 控制台输出响应状态码
// console.log(xhr.status)
reject(xhr.status)
}
}
}
})
p.then((value) => {
console.log(value)
}, (reason) => {
console.log(reason)
})
})
如何使用 Promise?
Promise API
Promised 构造函数
:Promise(executor) {}executor函数
:执行器,(resolve, reject) => {}resolve 函数
:内部定义成功时我们调用的函数 value => {}reject 函数
:内部定义失败时我们调用的函数 reason => {}- 说明:
executor执行器
会在Promise内部立即同步调用
,异步操作在执行器中执行
。
Promise.prototype.then
方法:(onResolved, onRejected) => {}onResolved 函数
:成功的回调函数 (value) => {}onRejected 函数
:失败的回调函数 (reason) => {}- 说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象
Promise.prototype.catch
方法:(onRejected) => {}onRejected 函数
:失败的回调函数 (reason) => {}- 说明:then()的语法糖,相当于:then(undefined, onRejected)
Promise.resolve
方法:() => {}- value: 成功的数据 或 promise对象
- 说明:返回一个成功/失败的promise对象
// 如果传入的参数为 非Promise类型的对象,则返回的结果为成功的promise对象
const p1 = Promise.resolve(521)
console.log(p1)
// 如果传入的参数为 Promise对象,则参数的结果决定了 resolve 的结果
const p2 = Promise.resolve(new Promise((resolve, reject) => {
// 如果参数为 Promise 对象,则外面的resolve的状态由里面的Promise对象的状态决定
// resolve(520) // 里面的Promise对象状态因为调用了 resolve,里面的状态变成了 'fulfilled'
reject('失败的结果') // 里面的Promise对象状态因为调用了 reject,里面的状态变成了 'rejected'
}))
p2.then((value) => {
console.log(value)
}, (reason) => {
console.log(reason)
})
Promise.reject
方法:() => {}reason:失败的原因
说明:返回一个失败的 promise 对象
// 与 Promise.resolve 方法相反,不管传入的是什么,最终返回的 Promise 对象的状态永远都是失败的,失败的结果为传入的对象(Promise对象/非Promise对象)
const p1 = Promise.reject(Promise.resolve(521))
p1.catch(err => {
console.log(err)
})
Promise.all
方法:(promises) => {}promises:包含n个 promise 的数组
说明:返回一个新的 promise,只有当所有的promise都成功才成功,只要有一个失败了就直接失败。
Promise.all(promises) {} 接收一个存放promise对象的数组,只有数组中的所有promise对象的状态都为'成功'时,返回的promise对象的状态才为'成功',返回promise对象的结果为所有promise对象的结果所组成的数组
只要数组中有一个promise对象的状态为'失败',返回的promise对象的状态就为'失败',返回的promise对象的结果为失败的promise对象的结果
// Promise.all(promises) {} 接收一个存放promise对象的数组,只有数组中的所有promise对象的状态都为'成功'时,返回的promise对象的状态才为'成功',返回promise对象的结果为所有promise对象的结果所组成的数组
const p1 = Promise.resolve('success01')
const p2 = Promise.resolve('success02')
const p3 = Promise.all([p1, p2])
/*p3.then((value) => {
console.log(value)
}, (reason) => {
console.log(reason)
})*/
// 只要数组中有一个promise对象的状态为'失败',返回的promise对象的状态就为'失败',返回的promise对象的结果为失败的promise对象的结果
console.log('---------------------------------分割线---------------------------------')
const p4 = Promise.reject('all方法失败演示') // 失败了,没有then的第二个方法onRejected或catch来处理,会有报错信息,不要惊讶
const p5 = Promise.all([p1, p2, p4])
console.log(p5)
Promise.race
方法:(promises) => {}- promises:包含n个promise的数组
- 说明:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
Promise.race(promises) 方法接收一个由 promise对象 组成的数组,返回一个promise对象。返回的promise对象的状态由最先执行的promise对象的状态决定,结果为最先执行的 promise 对象的结果。
// Promise.race(promises) 方法接收一个由 promise对象 组成的数组,返回一个promise对象。返回的promise对象的状态由最先执行的promise对象的状态决定,结果为最先执行的 promise 对象的结果。
const p1 = new Promise((resolve, reject) => {
reject('First Result.') // 让 promise 对象的状态为失败
// resolve("First Result.") // 让 promise 对象的状态为成功
/*setTimeout(() => {
reject(521)
}, 1000)*/ // 异步任务
})
const p2 = Promise.resolve("Second Result.")
const p3 = Promise.resolve("Third Result.")
const result = Promise.race([p1, p2, p3])
console.log(result) // 状态: fulfiiled, 结果:First Result.
Promise的几个关键问题
如何改变promise的状态?
resolve(value)
:如果当前是pending
就会变为fulfilled
。reject(reason)
:如果当前是pending
就会变为rejected
。- 抛出异常:如果当前是
pending
就会变为rejected
。
一个promise指定多个成功/失败回调函数,都会调用吗?
- 当promise改变为对应状态时
都会调用
- 当promise改变为对应状态时
改变promise状态和指定回调函数谁先谁后?
- 都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调
- 如何先改状态再指定回调?
- (1)在执行器中直接调用 resolve()/reject()
- (2)延迟更长时间才调用then()
- 什么时候才能得到数据?
- (1)如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
- (2)如果先改变的状态,那当指定回调时,回调函数就会调用,得到数据
promise.then()返回的新promise的结果状态由什么决定?
- 简单表达:由then()指定的回调函数执行的结果决定
- 详细表达:
- (1)如果抛出异常,新promise状态变为
rejected
,reason
为抛出的异常。 - (2)如果返回的是
非promise
的任意值,新promise变为fulfilled
,value为返回的值。 - (3)如果返回的是另一个新promise,此promise的结果就会成为新promise的结果
- (1)如果抛出异常,新promise状态变为
const p1 = new Promise((resolve, reject) => {
resolve('OK')
})
// promise.then()返回的新promise对象的结果由then所执行的回调函数的结果决定
const result = p1.then((value) => {
console.log(value)
// 1.抛出异常
// throw '出错啦!!!'
// 2.返回非promise对象的结果: 比如数字、字符串等
// return "521"
// 3.返回一个新的promise对象 -> then返回的promise对象的状态和结果由新的promise对象的状态和结果决定
return new Promise((resolve, reject) => {
// resolve('123') // then返回的promise对象的状态为成功
reject(789) // then返回的promise对象的状态为失败
})
}, (reason) => {
console.log(reason)
})
console.log("result: ", result)
promise如何串联多个操作任务?
- (1)promise的
then()
返回一个新promise,可以变成then()的链式调用。 - (2)通过
then
的链式调用串连多个同步/异步任务。
- (1)promise的
promise异常穿透是怎么回事?
- (1)当使用promise的then链式调用时,可以在最后指定失败的回调
- (2)前面任何操作出了异常,都会传到最后失败的回调中处理
中断promise链是怎么回事?
- (1)当使用promise的then链式调用时,在中间中断,不再调用后面的回调函数
- (2)办法:在回调函数中返回一个
pending
状态的promise对象
原型链
for ... of
和 for ... in
区别
说明
for ... of
循环其实是遍历迭代器的,之所以数组可以使用for ... of
, 对象不能使用for ... of
,是因为数组内部有iterator
属性,而对象没有。
for ... in
遍历对象
说明:
for ... of
遍历对象得到的每一项是键名
interface User {
id: number;
name?: string;
age: number;
email: string;
address: {
province: string;
city: string;
};
}
let user: User = {
id: 1,
name: "Zhou Yu",
age: 28,
email: "zhouyu2156@gmail.com",
address: {
province: "湖南省",
city: "长沙市",
},
};
for (const property in user) {
const key = property as keyof User;
console.log(property, user[key]);
}
for in
和 for of
遍历数组的区别
说明:
for ... in
遍历数组得到的是索引值for... of
遍历数组得到的是数组元素值
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// (1) for ... in
for (let index in arr) {
// for ... in 遍历数组得到的是索引值
console.log(index, arr[index]);
}
// (2) for ... of
for (let item of arr) {
// for... of 遍历数组得到的是数组元素值
console.log(item);
}
Web API
Ajax
生成器
说明:
顾名思义,生成器就是一个用于生成可迭代对象的函数。生成器通过
function
定义,在关键字后面加上*
来标识它是一个特殊的生成器函数。当调用此函数时,会返回一个可迭代对象,迭代对象即迭代器。每次调用可迭代对象的
next()
方法时,会执行生成器函数体中的代码,执行到yield
生成的内容后面,之后非阻塞挂起。等待下一次调用next()
方法,会继续从上一次yield
后面的代码执行,直到遇到下一个yield
。如此不断迭代,直到最后所有yield
执行完毕,属性done
变成true
表示生成器对象迭代完成。每次调用
next()
方法会返回一个对象{ done: <boolean>, value: <yield值> }
,该对象中包含着done
和value
两个属性,done
表示迭代器是否迭代完毕,value
表示迭代器yield
生成的值。
function* Book() {
console.log(1);
yield 1;
console.log(2);
yield 2;
console.log(3);
yield 3;
console.log(4);
}
const book = Book();
book.next(); // 打印 1, 返回 { done: false, value: 1 }
book.next(); // 打印 2, 返回 { done: false, value: 2 }
book.next(); // 打印 3, 返回 { done: false, value: 3 }
book.next(); // 执行最后的 conosle.log => 4, 此时已经迭代完成: { done: true, value: undefined }
console.log(book); // 依然是 { done: true, value: undefined }
yield *
迭代其他生成器生成的值
function* G1() {
yield 1;
yield* G2(); // yield * 返回其他生成器生成的值
}
function* G2() {
yield "hello";
}
for (const item of G1()) {
console.log("yield item: ", item);
}
拦截访问与修改
defineProperty
解释:
defineProperty
方法用于在一个对象身上定义一个新属性,或者修改一个对象的现有属性,并返回这个对象。它所定义对象的get
和set
方法会对所有属性的赋值和读取进行拦截操作。
- 原型:
defineProperty<T>(o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>): T;
interface User {
id: number;
name?: string;
age: number;
email: string;
address: {
province: string;
city: string;
};
}
let user: User = {
id: 1,
name: "Zhou Yu",
age: 28,
email: "zhouyu2156@gmail.com",
address: {
province: "湖南省",
city: "长沙市",
},
};
function _isObject(obj: unknown) {
return typeof obj === "object" && obj !== null;
}
function observe<T>(obj: T) {
for (const key in obj) {
let v = obj[key];
if (_isObject(v)) {
observe(v);
}
Object.defineProperty(obj, key, {
get() {
console.log(key, "读取操作");
return v;
},
set(val) {
if (val !== v) {
console.log(key, "更改操作");
v = val;
}
},
});
}
}
observe(user);
// 更改操作
user.name = "张三";
// 读取操作
console.log(user.name);
// 无法监听属性的新增和删除操作
delete user.name; // 删除以后,该属性绑定的getter/setter执行器也随之消失了
user.name = "李四"; // 赋值无法再执行拦截的操作
console.log(user.name); // 读取无法再执行拦截操作
Proxy
Proxy
代理对象,用于拦截对对象的访问操作。一旦访问对象就会触发通知。
interface User {
id: number;
name?: string;
age: number;
address: {
province: string;
city: string;
};
}
const user: User = {
id: 1,
name: "张三",
age: 18,
address: {
province: "北京",
city: "北京",
},
};
const proxy_user = new Proxy(user, {
get(target, key: keyof User) {
console.log(key, "读取操作");
return target[key];
},
set(target, key: keyof User, val) {
let v = target[key];
if (v !== val) {
v = val;
console.log(key, "赋值操作");
return true;
}
console.log("相同值, 不允许重新赋值");
return false;
},
deleteProperty(target, p) {
console.log(p, "删除操作");
return Reflect.deleteProperty(target, p);
},
});
proxy_user.age = 19; // 赋值操作
console.log(proxy_user.name, proxy_user.age); // 读取操作
Worker
介绍
worker 对象可以用来处理一些耗时且无关DOM操作的事情,例如:
- 处理复杂的数学计算
- 在后台整理大量的信息也不回阻塞主线程的运行
- 在后台处理图片数据
- 可以独立发送网络请求,获取数据等
流程: 创建 worker 对象
-> 绑定消息接收事件
-> 发送信息给 worker
-> worker 监听主线程发来的消息
-> worker处理完毕给主线程发送消息
<!-- 例如: 在一个 html 页面中这样写 -->
<script>
// 核心代码
/** 创建 worker 对象*/
const worker = new Worker('./worker.js')
/** 绑定事件: 监听 worker 返回的消息 */
worker.addEventListener('message', (event) => {
console.log("worker返回的结果", event.data)
})
/* 向 worker 发送消息 */
worker.postMessage(100)
</script>
// ./worker.js
var onmessage = (ev) => {
/** 接收主线程传来的数据 */
const count = ev.data;
console.log("我正在帮你计算", "请稍等...");
/** 计算从0到count的和 */
const sum = Array.from({ length: count}).fill(0).map((item,index) => item + index + 1).reduce((previousValue, currentValue) => previousValue + currentValue, 0)
/** 把计算结果返回给主线程 */
postMessage(sum)
}