JavaScript进阶技巧
# Promise
Promise是回调的升级版 , 在处理花费较长时间的任务时 , 使用 Promise 可以进行异步处理 , 防止堵塞
学习来源 : 点击跳转 (opens new window)
响应结构说明 :
- Prototype : 原型类型
- PromiseState : pending(等待) / fulfilled(完成) / rejected(拒绝)
- PromiseResult : 响应数据
案例 1: (检测图片有效)
const imageUrl = '';
const imgPromise = (url) => {
return new Promise( (resolve, reject) => {
const img = new Image();
img.src = url;
// 加载成功
img.onload = () => {
resolve(img);
}
img.onerror = () => {
reject(new Error('图片有误'));
}
} );
};
imgPromise(imageUrl)
.then( img => {
console.log('success : ',img)
})
.catch(err => {
console.log('error: ',err)
})
案例2 : (随机数判断)
new Promise((resolve, reject) => {
setTimeout(() => {
let num = Math.floor(Math.random() * 11);//0-10的随机数
if (num >= 5) {
resolve(num);
} else {
reject(num);
}
},1000)
}).then(res => {
console.log("执行了成功时的回调 , 数值为:"+ res);
}).catch(err => {
console.log("执行了失败时的回调 , 数值为:"+ err);
})
# Promise方法
# Promise.all()
all()方法 用于处理多个异步任务
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果1');
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果2');
}, 2000);
})
//Promise.all([])接收多个异步任务 , 放入数组中
Promise.all([p1, p2]).then(results => {//results接收多个参数 , 所以是数组
console.log(results);//["结果1", "结果2"]
})
# Promise.race()
race()方法 当中的任务谁先完成就执行谁
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果1');
}, 1000);
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('结果2');
}, 2000);
})
//Promise.race([])中接收多个异步任务 , 放入数组中
Promise.race([p1, p2]).then(result => { //p1和p2 公用同一result , 谁先完成接收谁
console.log(result);
})
# Promise手写
学习来源 : 点击跳转 (opens new window)
class Commitment {
// 状态
static PENDING = '待定';
static FULFILLED = '成功';
static REJECTED = '拒绝';
// 构造方法
constructor(func) {
this.status = Commitment.PENDING;
this.result = null;
// 保存函数
this.resolveCallbacks = [];
this.rejectCallbacks = [];
// 传入异常对象进行操作
try {
func(this.resolve.bind(this), this.reject.bind(this));
} catch (err) {
this.reject(err);
}
}
// 失败
resolve(res) {
// 事件后执行
setTimeout(() => {
if (this.status === Commitment.PENDING) {
this.status = Commitment.FULFILLED;
this.result = res;
this.resolveCallbacks.forEach(call => {
call(res)
});
}
});
}
// 成功
reject(res) {
// 事件后执行
setTimeout(() => {
if (this.status === Commitment.PENDING) {
this.status = Commitment.REJECTED;
this.result = res;
this.rejectCallbacks.forEach(call => {
call(res)
});
}
});
}
then(onFULFILLED, onREJECTED) {
return new Commitment((resolve, reject) => {
// 不是函数则传递空函数
onFULFILLED = typeof onFULFILLED === 'function' ? onFULFILLED : () => { };
onREJECTED = typeof onREJECTED === 'function' ? onREJECTED : () => { };
// 如果外部也使用了异步 , 很有可能还是 待定状态
if (this.status === Commitment.PENDING) {
this.resolveCallbacks.push(onFULFILLED);
this.rejectCallbacks.push(onREJECTED);
}
if (this.status === Commitment.FULLFILLED) {
// 异步处理
setTimeout(() => {
onFULFILLED(this.result);
})
}
if (this.status === Commitment.REJECTED) {
// 异步处理
setTimeout(() => {
onREJECTED(this.result);
});
}
});
}
}
// 测试实例
console.log('No.1');
let commitment = new Commitment((resolve, reject) => {
console.log('No.2');
setTimeout(() => {
reject('这次一定');
console.log('No.4');
});
});
commitment.then(
res => { console.log(res) },
err => { console.log(err) }
)
console.log('No.3');
测试步骤说明 : (按照No.x步骤说明)
- (待定) new实例
- (待定) 异步操作 , 执行 then() , 由于是待定状态进行 数组保存函数(resolve/reject)
- (成功) 执行 reject() 里面还有一个异步处理(外部先执行了)
- (成功) 执行外部异步 , 最后和 reject() 共同跑完
# Promise问题
# Fetch 请求
Fetch 和 ajax 一样能实现异步请求 , Fetch 请求是基于 Promise对象(响应为Promise)
Fetch 和 ajax 区别 :
- 当接收到错误HTTP状态码(404/500)时 , 并不会进行 reject()处理(关闭网络才会进行该处理) , 但会在 fetch()处理中返回 Promise对象ok属性为 false 的异常
- fetch() 可以接收跨域 cookies , 也可 建立跨域会话
- fetch() 不会发送 cookies . 需要自行修改请求头(下面有)
MDN文档 : 点击跳转 (opens new window)
学习来源 : 点击跳转 (opens new window)
基本语法 :
fetch(url, options).then( res =>{
//处理http响应
})
.catch( err =>{
//处理错误
})
Response对象响应结构 : (主要关注 PromiseResult的Response对象 响应数据)
属性 | 类型 | 说明 |
---|---|---|
body | Object (ReadableStream) | 响应体 |
bodyUsed | Boolean | |
headers | Object (Headers) | 响应头 |
ok | Boolean | 是否成功 |
redirected | Boolean | 是否发生跳转 |
status | Number | http状态码 |
statusText | String | 返回状态文字描述 |
type | String | 返回请求类型 |
url | String | 来源url |
注意 :
- fetch默认不带cookie 传递cookie时 , 必须在header参数内加上 ==credentials: 'include'== , 才会像 xhr 将当前cookie 带有请求中
- 请求成功返回 Response对象 / 失败则是 TypeError对象
- 请求异常捕获操作一般 使用await 搭配 try-catch异常捕获
# Get
fetch(`url`)
.then(response => response.json())
.then(data => console.log(data));
# Post
fetch('url',{
method: 'POST',
body: JSON.stringify({name:'Sans',age:20}),
headers: { 'Content-Type': 'application/json' }
})
.then(response => response.json())
.then(data => console.log(data));
# Async Await 异步
Async 是用于声明异步 function函数 ; Await 是用于等待 异步方法执行完成
Async Await函数 一般会返回Promise对象 (也是基于Promise延伸)
// 返回的是 Promise对象
async function shwo() {
// 这两条返回是同理的
// return Promise.resolve('Sans');
return 'Sans';
}
shwo().then(value =>{
console.log(value); // Sans
})
实例 :
异步最大的痛点就是在多个共同运行时 , 顺序不能同步执行 , 例如以下代码 , 每次执行结果的顺序不一样
const url = "https://gorest.co.in/public/v1/users"
function show(){
fetch(`${url}/1/`)
.then(res => res.json())
.then(json => json.data)
.then(data => console.log(`${data.name}`))
fetch(`${url}/2/`)
.then(res => res.json())
.then(json => json.data)
.then(data => console.log(`${data.name}`))
fetch(`${url}/3/`)
.then(res => res.json())
.then(json => json.data)
.then(data => console.log(`${data.name}`))
}
show()
Async Await 解决痛点
const url = "https://gorest.co.in/public/v1/users"
async function show(){
let res1 = await fetch(`${url}/1/`)
let json = await res1.json();
console.log(json.data.name)
let res2 = await fetch(`${url}/2/`)
let json2 = await res2.json();
console.log(json2.data.name)
let res3 = await fetch(`${url}/3/`)
let json3 = await res3.json();
console.log(json3.data.name)
}
show()
await 会等待请求 , 等待的是当前 async修饰的函数 , 并不会影响主线程
# Web存储
# Cookie
Cookie 是服务器发送到浏览器中的本地小块数据(4Kb大小) , 由于浏览器访问的HTTP是无状态的 , 可以通过Cookie 记录状态信息
MDN文档 : 点击跳转 (opens new window)
Cookie应用方面 :
- 会话状态管理 (登录状态/记住密码/...)
- 个性化设置 (自定义设置/主题/...)
- 浏览器行为跟踪
Cookie 是早期开发的 , 存储小 , 每次请求都携带(额外开销) , Cookie 也逐渐被淘汰
存储形式 : 名值对字符串形式存储
操作示例 :
// 存储
let key = "Sans";
let value = encodeURIComponent("");
document.cookie = `${key}=${value}`;
// 提取
// 分割结果 [['user',"xxx"],['Jone','xxx']]
let array = document.cookie
.split(';') // 分割结果 ['Sans=xxx','Jone=xxx']
.map( cookie => cookie.split('=') );
value 进行 URL编码 是为了防止输入 非法字符(空格/分号/...) 问题
有效期设置 :
只需在后面添加参数 max-age=时间量
(单位s)
// 两天
let twoDays = 2 * 24 * 60 * 60;
document.cookie = `${key}=${value}; max-age=${twoDays}`;
# localStorage
永远存储至浏览器(可手动删) , 有同步机制(影响渲染)
MDN文档 : 点击跳转 (opens new window)
存储形式 : 键值对字符串形式存储
操作示例 :
// 存储
localStorage.setItem('myCat', 'Tom');
// 提取
let cat = localStorage.getItem('myCat');
// 移出
localStorage.removeItem('myCat');
// 清空
localStorage.clear();
# sessionStorage
存储于 当前浏览器会话 , 一旦关闭则失效(安全性高)
MDN文档 : 点击跳转 (opens new window)
存储形式 : 键值对字符串形式存储
操作示例 : 和localStorage操作方式一致
注意 :
- 页面一旦刷新 sessionStorage 就失效
- 浏览器另开一个新窗口(每个页面都有各自的sessionStorage) , sessionStorage 也不会共享
# Web存储总结
cookie | localStorage | sessionStorage | |
---|---|---|---|
大小 | 4Kb | 10Mb | 5Mb |
兼容 | H4/H5 | H5 | H5 |
访问 | 任何窗口 | 任何窗口 | 同一窗口 |
有效期 | 手动设置 | 无 | 窗口关闭 |
存储位置 | 浏览器&服务器 | 浏览器 | 浏览器 |
与请求一同发送 | Y | N | N |
语法 | 复杂 | 简易 | 简易 |
# call & apply & bind 构造
JavaScript函数都是 Funcation对象 是构造函数 , 构造函数有 Funcation.prototype原型对象 , 原型对象 里面包含有很多属性(call)
# call
call()方法 指定的 this 值和单独给出的 一个/多个 参数来调用一个函数
意图 : call() 实现在不同对象 分配/调用 一个对象的 函数/方法
MDN文档 : 点击跳转 (opens new window)
**语法 : ** ==function.call(thisArg, arg1, arg2, ...)==
参数 | 选择 | 说明 |
---|---|---|
thisArg | 可选 | 在 function函数 运行时使用的 this 值 |
arg... | 可选 | 传递的数据 |
实例可以看以上链接文档
# call()手写
function person( a , b , c , d){
return {
name: this.name,
a: a, b: b, c: c, d:d
}
}
var people = { name:'Sans' };
Function.prototype.newCall = function (obj) {
// 对象obj不存在指向window
var obj = obj || window;
// this指定的并非为 person , 因此需要绑定
obj.p = this;
let newArray = [];
for(let i = 1 ; i < arguments.length ; i++){
// 最终结构 [ 'arguments[1]', 'arguments[2]', 'arguments[3]', 'arguments[4]' ]
newArray.push('arguments[' + i + ']')
}
// 执行方法
var res = eval('obj.p('+newArray+')');
delete obj.p;
return res;
};
// 测试代码
let p = person.newCall( people , 'No.1' , 'No.2' , 'No.3' , 'No.4');
console.log(p);
# apply
apply() 方法语法作用和 call()相同 , 唯独区别在 接收的是参数列表
MDN文档 : 点击跳转 (opens new window)
语法 : ==apply(thisArg, argsArray)==
参数 | 选择 | 说明 |
---|---|---|
thisArg | 可选 | 在 function函数 运行时使用的 this 值 |
argsArray | 可选 | 数组/类数组对象 |