学习一 异步和同步
同步 即按顺序一个接一个执行
异步 是当程序中出现等待时间较长的(需要计算、需要等待反馈、等待延时等等)任务,为防止执行异步任务时程序阻塞(程序干等着什么也不做),异步任务在被创建后不会进入主线程,需要先在任务队列中等待,当主线程任务执行完毕,任务队列才会“通知”主线程执行异步任务
异步任务分为宏任务和微任务
- macro-task(宏任务):包括整体代码script,setTimeout,setInterval
- micro-task(微任务):Promise,process.nextTick
( Promise并不是完全的同步,在Promise中是同步任务,执行resolve或者reject回调的时候,此时是异步操作,会先将then/catch等放到异步任务中的微任务队列 )
执行过程:
1.先执行所有同步任务,碰到异步任务放到任务队列中
2.同步任务执行完毕,开始执行当前所有的异步任务
3.先执行任务队列里面所有的微任务
4.然后执行一个宏任务
5.然后再执行所有的微任务
6.再执行一个宏任务,再执行所有的微任务·······依次类推到执行结束。
3-6的这个循环就称为事件循环Event Loop
// 异步请求
const A = new Promise((resolve,reject) => {
resolve('成功')
}.then(data => {
// 异步请求嵌套
return new Promise((resolve,reject) => {
resolve('成功2')
}.then(data => { // then 的第一个参数data
console.log( data )
},err =>{ // then 的第二个参数err
console.log( err )
//抛出异常 后续任务全部触发失败处理
throw new Error ( '任务失败’)
}
}
学习二 async、await
async (await)是基于promise的一种异步操作的简化功能,async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖,用同步的书写方式实现了异步的代码。(执行第一步,将执行第一步的结果返回给第二步使用。)
async getFaceResult () {
try {
let location = await this.getLocation(this.phoneNum);
if (location.data.success) {
let province = location.data.obj.province;
let city = location.data.obj.city;
let result = await this.getFaceList(province, city);
if (result.data.success) {
this.faceList = result.data.obj;
}
}
} catch(err) {
console.log(err);
}
}
//先准备一个返回 promise 对象的函数
function asyncTask ( ) {
return new Promise ( resolve,reject ) => {
//关键代码执行结果
const isSuccess = true
if ( isSuccess = true ){
resolve ('任务的处理结果data')
} else {
reject( '失败')
}
}
}
//为使用 await 的函数添加 async,增加逻辑可读性
async function main(){
console.log('任务一')
const data1 = await asyncTask()
console.log('任务二')
await asyncTask2()
console.log('任务三')
task()
console.log('任务四')
await asyncTask3()
}
main()
学习三 proxy代理
proxy是一种网络服务器,充当客户端和目标服务器之间的中介,为客户端提供匿名性、安全性和加速访问等服务。
一、改善网络性能:
proxy可以压缩页面内容,减少传输数据量,同时缓存网页、图像、视频等内容,当用户再次访问该内容时,proxy就可以从缓存中读取,从而避免了重复下载和重复请求,提高了访问速度。proxy还能够进行负载均衡,将用户请求分配到多个服务器上,避免出现某一个服务器负载过高的情况。
二、保障网络安全
proxy可以屏蔽客户端与目标服务器之间的直接联系,保护客户端真实IP地址和身份信息,防止客户端暴露在公网上遭受攻击(实现匿名访问)。proxy还可以对访问进行过滤和检查,拦截恶意代码、病毒和垃圾邮件等,保护网络安全的同时实现安全访问控制,限制用户访问某些不安全或敏感内容,保护重要的企业数据,防止数据泄露。(proxy可以将用户请求转发到国外服务器,从而突破网络限制,实现自由访问。)
const target = {
message: "Hello, world!",
foo: function() {
return "foo";
}
};
const handler = {
get: function(target, prop, receiver) {
if (prop === "message") {
return "Hello, proxy!";
}
// 对于非'message'属性,直接返回原属性
return Reflect.get(...arguments);
}
const proxyTest = new Proxy(target, handler);
console.log(proxyTest.message); // 输出 "Hello, proxy!"
console.log(proxyTest.foo()); // 输出 "foo"
在这个示例中,我们创建了一个 Proxy 对象来代理 target 对象。我们通过 handler 对象定义了一个 get 捕获器,它拦截了对 message 属性的访问,并返回了一个不同的字符串。对于其他属性的访问,我们使用 Reflect.get 来转发请求到目标对象。
然后我尝试对foo也进行拦截更改,这是尝试时的错误代码
// 与get同级
apply: function(target, thisArg, argumentsList) {
// 拦截foo方法的调用
if (thisArg === target.foo) {
return "intercepted foo!";
}
// 对于其他函数的调用,直接调用原函数
return Reflect.apply(...arguments); }
};
但总是失败。
百度的ai总是说一些囫囵话,做一些莫名其妙的修改,导致我一度以为是判断条件有误,直到最后发现根本没有进入apply方法。
换了个ai,才找到问题
Proxy 对象中,apply 方法只会在代理对象作为函数被调用时触发拦截,而不是在方法调用时触发。且apply 的 target 参数指向被代理的对象。因此,如果你想要使用 apply 来处理函数调用,你需要确保代理对象本身是一个函数,或者你通过某种方式直接调用了代理对象。
我们无法直接从 apply 的参数中获取被调用函数的名称,但get可以,这些调用会通过 get 陷阱来获取方法的引用,然后直接调用这些方法。例如,如果 proxy 是一个代理一个对象的 Proxy 对象,那么 proxy.foo() 会触发 get 陷阱来获取 foo 方法的引用,然后直接调用 foo 方法。
关于代理对象(Proxy object)和被代理对象(Target object)的 代码示例
const targetObject = {
data: 42,
method: function() {
return 'Called method on target object';
}
};
const handlerObject = {
get: function(target, propKey, receiver) {
console.log(`Property ${propKey} has been accessed.`);
return Reflect.get(...arguments);
},
apply: function(target, thisArg, argumentsList) {
console.log('Method has been called with arguments:', argumentsList);
}
};
const proxyObject = new Proxy(targetObject, handlerObject);
console.log(proxyObject.data); // 通过代理对象访问属性,触发 get 陷阱
proxyObject.method(); // 通过代理对象调用方法,触发 apply 陷阱
在这个示例中:
targetObject是被代理对象,它包含了一些属性和方法。
proxy是一个构造函数(类)new Proxy(obj, handler),用于定义基本操作的自定义行为(如属性查找、删除、赋值、枚举、函数调用等)
-
handlerObject是一个包含get和apply陷阱的对象,用于自定义属性访问和方法调用的行为。handler (target,property,receiver)(第一个参数是代理对象,第二个参数是属性名,第三个对象是基于当前实例,做一些proxy对象的操作) -
proxyObject是代理对象,它是通过new Proxy(targetObject, handlerObject)创建的,它包装了targetObject并在访问属性或调用方法时触发handlerObject中定义的陷阱。(第一个参数是代理对象,第二个参数是要进行的操作)
在 apply 中处理函数调用的正确操作
const target = function () {};
const handler = {
apply: function (target, thisArg, argumentsList) {
console.log(`Function is being called with arguments`, argumentsList);
const result = target.apply(thisArg, argumentsList);
console.log(`Function has been called`);
return result;
}
};
const proxyTest = new Proxy(target, handler);
proxyTest.foo = function () {
return "foo";
};
proxyTest.bar = function () {
return "bar";
};
proxyTest("foo"); // 触发 apply 陷阱,并输出 "foo"
proxyTest("bar"); // 触发 apply 陷阱,并输出 "bar"
最后 ai 还给出了解决办法
办法一 返回一个函数包装器
即通过创建一个新的函数来“包装”原始函数,然后在新函数中调用原始函数。
包装函数在调用原始函数时会调用原始对象上的方法,并修改其行为,执行一些额外的操作,比如添加额外的日志、修改参数或返回值等。
const handler = {
get: function (target, prop, receiver) {
// 拦截message属性的访问
if (prop === "message") {
console.log(prop, 1);
return "Hello, proxy!";
} else if (prop === "foo") {
console.log(prop, 2);
// 返回一个函数包装器来拦截foo的调用
const origFoo = target[prop];
return function (...args) {
console.log('foo is being called');
// 调用原始的foo函数并获取其返回值
let result = origFoo.apply(this, args);
// 返回一个更改后的值
return result = "intercepted foo: next";
};
}
// 对于其他属性,正常返回它们的值
return Reflect.get(...arguments);
}
};
const proxyTest = new Proxy(target, handler);
console.log(proxyTest.foo());
当 foo 属性被访问时,get 陷阱会返回一个新的包装函数。包装函数首先调用原始的 foo 方法,获取其返回值,然后你可以在这个基础上添加额外的逻辑或修改返回值。这种方法允许你保留原始方法的行为,同时添加额外的功能。
// 如果有多个函数要被代理,先检查属性是否是函数,如果是,则返回一个函数包装器
if (typeof target[prop] === 'function') {
// 获取原始的函数
const origMethod = target[prop];
// 返回一个包装器函数
return function (...args) {
// 在原始函数执行前添加的逻辑
console.log(`${prop} is being called`);
// 调用原始的函数并获取其返回值
const result = origMethod.apply(this, args);
// 在原始函数执行后添加的逻辑
console.log(`${prop} has been called`);
// 返回原始函数的返回值,也可以修改它或返回一个新的值
return result;
};
}
console.log(proxyTest.foo());
console.log(proxyTest.bar());
办法二 实际上是创建了一个全新的函数
要拦截对 foo 方法的访问,你需要在 get 中对 foo 方法进行特殊处理。即在 get 中返回一个函数,这个函数会在 foo 被访问时执行。这样,当你尝试访问 proxyTest.foo() 时,实际上是在调用 get 返回的函数,而不是原始的 foo 方法(除非你在新函数中显式调用它)。
const target = {
message: "Hello, world!",
foo: function () {
return "foo";
}
};
const handler = {
get: function (target, prop, receiver) {
// 拦截message属性的访问
if (prop === "message") {
return "Hello, proxy!";
}
// 拦截foo属性的访问,并返回一个自定义函数
if (prop === "foo") {
return function () {
return "foo proxy";
};
}
// 对于其他属性,正常返回它们的值
return Reflect.get(target, prop, receiver);
}
};
const proxyTest = new Proxy(target, handler);
console.log(proxyTest.message);
console.log(proxyTest.foo());
学习四 模块 module
两种模块范式 EMS(浏览器) CommonJS(Node.js)
首先在根模块(index.js)引入的script标签内标记 type = 'module'
EMS应用方法
一 export default
- 模块文件中 export default { } 导出
- 应用文件内 import xxx 导入 (xxx是新名字)
二 export
- 模块文件中 export 导出
- 应用文件内 import { xxx } 导入 (xxx是原名,如果希望改名,应该写 xxx as yyy , yyy是新名)
CommonJS应用方法
- 模块文件中 module.exports = { } 导出,或 exports.xxx = ''
- 应用文件内 const xxx = require( ../文件路径) 导入 (xxx是新名字)