JavaScript
# JavaScript
# ES6
# Symbol-新数据类型
Symbol是JavaScript的第七种数据类型。常用于表示独一无二的字符串,例如函数名等。
# 定义
Symbol()局部,相同的串并不代表是同一个Symbol。
Symbol.for()全局,开辟内存空间,相同的串代表是同一个Symbol。
# 描述
symbol.description:通用。Symbol.keyfor():由Symbol.for()建立的对象独享。
# 用途
唯一的key。
对象私有属性。
遍历时,将无法取到以Symbol对象为key的属性。
# 注意事项
for...in取不到以Symbol对象为key的属性。Object.getOwnPropertySymbols()可以拿到以Symbol对象为key的属性。Reflect.ownKeys()能够获取所有属性。
# 数据结构
# Set
类似数组,但所有value唯一。
常用
- 属性:
size - 方法:
- 增:
add - 查:
has - 删:
delete clear:清空。values:获得Set迭代对象。
- 增:
与数组互转
- 转数组:
Array.from()、[...set] - 转Set:
new Set(arr)
# WeakSet
和Set差不多,但是WeakSet有几个不同点:
只能存放引用数据类型。
WeakSet的对象是弱引用。WeakSet对对象的引用不会被考虑进垃圾回收机制,只要没有引用该对象,该对象就会被回收,无论是否在WeakSet中被引用。因此容易被弱引用造成影响的方法都不被提供,如values、size、for of等。const objs = new WeakSet(); let obj = { qq: "123" }; objs.add(obj); objs.add({ a: "123", b: { c: "hello", }, }); console.log(objs); setTimeout(() => { console.log(objs); }, 5000);1
2
3
4
5
6
7
8
9
10
11
12
13
# Map
类似对象,但所有key唯一,且key可以是任意值(对象key本质是字符串)。
常用
- 属性
- 方法
- 增:
set,key同样时,即为更改。new Map([[key, value], [], ...])
- 删:
delete - 查:
has、get
- 增:
- 遍历
entries,可以解构一下[key, value]keysvaluesfor...offorEach
与数组互转
- 转数组:
[...map] - 转map:
new Map([[key, value], [], ...])
# WeakMap
与map差不多,但键key必须是引用数据类型,且WeakMap是弱引用类型。
变化:size、keys等方法都用不了,因为是弱引用,不加入垃圾回收机制。
# 运算符的扩展(ES6)
# 指数运算符**
num1 ** num2 = num1^num2
// 右结合
num1 ** num2 * num3 = num1^(num2^num3)
// 新赋值运算符
num1 **= 3 // num1 ^3
2
3
4
5
# 链判断运算符?.
三种用法:
对象属性
obj是否存在?obj.obj2是否存在?obj.obj2.data是否存在?顺序着来,不存在直接返回undefined。const data = obj?.obj2?.data || {}1对象方法
obj?.func()1函数调用
func?.()1
# Null判断运算符??
通常赋默认值是以||方式提供,但 如null、undefined、NaN、0、""、false也会出现会被囊括其中。Null判断运算符??只有运算符左侧的值为undefined、null时才会返回右侧的值。
const num = 0 ?? 1 // num = 0
const num = obj?.num ?? 1 // num = 1
2
# 文字翻转
split('').reverse().join('')
# 类型判断
# typeof
type of 1 // number
type of '1' // string
type of {} // object
type of [1, 2, 3] // object
type of function run() {} // function
type of hdcms // hdcms未定义情况下:undefined
type of hdcms // hdcms已声明情况下:undefined,因为初始值为undefined
2
3
4
5
6
7
# instanceof
[] instanceof Array // true
{} instanceof Object // true
2
###Object.prototype.toString.call(obj)
升级版typeof,更强大。
# 同源、跨域页面间通信
# 同源页面
同源:协议、url、端口号一致。
A页面
window.addEventListener('storage', (e)=>{ console.log(e) })1
2
3B页面
localStorage.setItem(key, value)1
# 跨域页面
跨域:协议、url、端口号至少一个不一样。
A页面
window.addEventListener('message', (e) => { console.log(e) // e.origin 查看源地址 })1
2
3
4B页面
const targetWindow = window.open('http://localhost:10001/user'); setTimeout(()=>{ targetWindow.postMessage('来自10002的消息', 'http://localhost:10001') }, 3000)1
2
3
4
# 内存泄漏
不再用到的内存,没有及时释放,叫做内存泄漏。
# 引起的原因
- 全局变量
- 定时器
- 闭包使用不当
- 网络回调函数
- DOM元素(js、DOM树都清理)
# Garbage Collection(GC)
标记清除法
根对象开始寻找可引用的对象,未被引用的对象移出。
引用计数法
新引用+1,移出引用-1,引用为0的对象回收。
# Websocket
# 基本用法
# 方法
close():主动关闭连接。send():客户端想服务端发送数据。
# 事件
close:连接断开触发。error:连接错误触发。message:收到服务端发送的数据。open:打开连接时触发。
例子
const ws = new WebSocket(url)
// 建立连接
ws.addEventListener('open', () => {
ws.send('hello server') // 向服务端发送数据
})
// 接受消息
ws.addEventListener('message', (e) => {
// e.data
})
// 断开连接
ws.addEventListener('close', () => {
})
// 断开连接
ws.addEventListener('error', () => {
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
对于后端,每创建一个新的连接,都会有一个conn对象。
# 全局变量污染
立即执行函数
(function (window) { function show() { console.log(`js2.js ---`); } window.js2 = { show, }; })(window);1
2
3
4
5
6
7
8
9块作用域
{ let show = function () { console.log(`js1.js ---`); }; window.js1 = { show, }; }1
2
3
4
5
6
7
8
9
# Web Workers
# 基本使用
检测浏览器是否支持
Web Workersif(window.Worker) {}1创造一个
workerconst worker1 = new Worker(aURL, options)1此
worker指定一个脚本来执行线程。脚本里(main.js)可以写一个事件处理函数作为响应
onmessage = function (e) { // e.data postMessage(data); }1
2
3
4向
worker发送数据worker1.postMessage(data)1监听
worker返回的消息worker1.onmessage = function (e) {}1杀掉
workerworker1.terminate()1
# String字符串常用方法
# 查找
indexOf、lastIndexOf(char, loc)includesstartsWith(str)是否以str开头。
# 截取
substr(start, num)slice(start, end)subString(start, end):start和end为非负的整数。
# 替换
replace(word, replaceWord)
# 重复
str.repeat(num)
# Array数组常用方法
# 检测
Array.isArray()
# 数组与字符串互转
# 转为字符串
join()String()、toString()默认间隔英文逗号“,”。
# 转为数组
split()Array.from(obj, map)[...arr] = str原理解构赋值+rest操作符。str本身是有 length属性的字符串,所以每个字符都放到了变量arr里。
# 元素操作
# 添加
pushunshift添加到数组头部。
splice(start, delNum, ele1,...)
# 删除
popshift删除数组尾部元素。
splice(start, delNum)
# 移动
splicefunction itemMove(arr, from, to) { arr.splice(to, 0, ...arr.splice(from, 1)); return arr; }1
2
3
4
# 替换
以obj替换已有数组[start, end)所有元素。
arr.fill(obj, start, end)
# 清空数组
arr = []- 推荐:
arr.length = 0 arr.splice(0, arr.length)
# 创建数组
Array.of(params,...)const arr = Array.of(1, 2, 3, true, "123");1Array.from()通过拥有 length 属性的对象或可迭代的对象来返回一个数组。
Array.from(obj, mapFunc, this)1可实现浅拷贝,如
const arr = Array.from([{a: '1'}, 'b'])1new Array()仅传一个参数num,即创建num个为undefined的元素。多个参数与
Array.of一致。
# 数组截取
slice截取数组[start, end)部分,返回一个新数组。不对原数组操作。
arr.slice(start, end)1注意
- 只传一个参数时,end代表
arr.length。 - slice(0)是浅拷贝!!!
- 只传一个参数时,end代表
splice此函数会修改数组本身,返回被修改的内容。
arr.splice(start)1
# 查找
以下查找,针对数组里的对象都是“址”查找,不是一样的地址,不会匹配。
indexOf查找在数组中某一指定元素(必须
===)的第一次出现的位置。返回index、-1。arr.indexOf(obj)1includes判断一个数组是否包含一个指定的值,返回true、false。
arr.includes(ele)1find查找通过测试的第一个值,返回查到的值、undefined。
arr.find(item => item > 18) // 手撕 Array.prototype.find(callback) { for(let item of this) { if(callback(item)) return item; } return undefined; }1
2
3
4
5
6
7
8
9findIndex查找通过测试的第一个值,返回查到的值的索引、-1。
arr.findIndex(item => item > 18)1some查找是否有通过测试的值,返回true、false。
arr.some((item, index) => item > 18)1every类some,查找是否都有通过测试,返回true、false。
filter过滤不符合条件的元素,返回一个新数组(浅拷贝)。
arr2 = arr.filter(item => item > 18)1原理:
Array.prototype.filter = function (callback) { const arr = []; for (const iterator of this) { callback(iterator) && arr.push(iterator); } return arr; };1
2
3
4
5
6
7
# 合并
arr.concat(array2,...)返回一个新数组,将arr、arr2,...连接。传入的参数如果是数组,将被展开一层。如果是非数组,将直接作为元素添加。
[...arr1, ...arr2]Object.assign()
# 排序
arr.sort((a,b) => a-b) // 小于0升序,大于0降序
原理:
Array.prototype.swap = function (index_a, index_b) {
const box = this[index_a];
this[index_a] = this[index_b];
this[index_b] = box;
};
Array.prototype.sort = function (callback) {
for (let i = 0; i < this.length; i++) {
for (let j = i + 1; j < this.length; j++) {
if (callback(this[i], this[j]) > 0) {
this.swap(i, j);
}
}
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 翻转
arr.reverse()
# 集合运算
# 去重
[...new Set(arr)]
# 交集
function show(arr1, arr2) {
const arr = [];
const set1 = new Set(arr1);
const set2 = new Set(arr2);
return [...set1].filter((item) => set2.has(item));
}
2
3
4
5
6
# 并集-交集
function show(arr1, arr2) {
const set1 = new Set(arr1);
const set2 = new Set(arr2);
const fn = (s1, s2) => {
const arr = [];
for (let item of s1) {
if (!s2.has(item)) {
arr.push(item);
}
}
return arr;
};
const res1 = fn(set1, set2);
const res2 = fn(set2, set1);
return [...res1, ...res2];
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 差集
arr1.filter((item) => !new Set(arr2).has(item))
# 高阶
累计:
array.reduce(function(pre, currentValue, currentIndex, arr), initialValue)参数:
pre是上次的返回值,第一次为initialValue ?? array[0]。原理
Array.prototype.reduce = function (callback, initValue) { // 检查数组是否为null或undefined if (this == undefined) throw new TypeError("this is null or undefined"); // 检查callback是否是函数 if (typeof callback !== "function") throw new TypeError(`${callback} is not a function`); const arr = Object(this); // 确保arr为对象 const arrLength = arr.length >>> 0; // 确保length为正数 let index = 0; // 第一个有效值索引 if (initValue === undefined) { // 寻找第一个有效值 while (index < arrLength && !(index in arr)) index++; // index超出数组范围,证明是空数组 if (index >= arrLength) { throw new TypeError("empty array"); } // 设置初始值 initValue = initValue ? initValue : arr[index++]; } let res = initValue; // 初始化结果 // 计算结果 for (let i = index; i < arrLength; i++) { if (i in arr) res = callback(res, this[i], i, this); } return res; };1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28映射:
map原理
Array.prototype.map = function (callback = (item) => item) { const newArr = []; this.forEach((element) => { newArr.push(callback(element)); }); return newArr; };1
2
3
4
5
6
7
# 对象
# 属性
# 属性特征
设置属性特征
Object.defineProperty(obj, property, { writable: false, // 不可写 enumerable: false, // 不可遍历 configurable: false, // 不可配置、删除 value }); Object.definePropertys(obj,{property1, property2,...})1
2
3
4
5
6
7
8读取属性特征
Object.getOwnPropertyDescriptor(obj, propertyStr) Object.getOwnPropertyDescriptors(obj)1
2
3
# 删除属性
仅会删除对象里的属性,不会删除原型链上的。
delete obj.property
# 禁止添加属性
Object.preventExtensions(obj)
检测是否禁止:Object.isExtensible()
# 封闭对象
禁止添加属性,且对象不可配置
Object.seal(obj)
检测是否被封禁:Object.isSealed()
# 冻结对象
对象不能被修改,不可增删属性、不可配置、不可写,原型不可修改。
Object.freeze(obj)
# 检测某属性是否存在
hasOwnProperty:不查原型链,只查询对象 in:查原型链
# 访问器
const data = {
set property(value) {
},
get property() {
}
}
2
3
4
5
6
7
8
注意,set和打点访问赋值同时针对一个属性,set优先。
# 遍历
for in
# 拷贝
浅拷贝
Object.assign({}, obj1, obj2){...obj1, ...obj2}Object.create()可以保证原型一样。
const obj2 = Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj) );1
2
3
4
深拷贝
老版
function cloneDeep(obj) { if (Array.isArray(obj)) { const arr = []; for (const ele of obj) { arr.push(cloneDeep(ele)); } return arr; } else if (obj instanceof Object) { const copy_obj = {}; for (const key in obj) { // for in会遍历所有可枚举属性 if (obj.hasOwnProperty(key)) { copy_obj[key] = cloneDeep(obj[key]); } } return copy_obj; } else { return obj; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20新版
function cloneDeep(obj) { if (obj instanceof Object) { const res = Array.isArray(obj) ? [] : {}; for (const [key, value] of Object.entries(obj)) { res[key] = cloneDeep(value); } return res; } else { return obj; } }1
2
3
4
5
6
7
8
9
10
11
# 创建对象
Object.create(prototype, properties)
# if()表达式和==原理
# if()
if(1) // true ---> if(Boolean(1))
if(undefined) // fasle
if({}) // true
if([]) // true ---> if(Boolean([]))
2
3
4
if()里,其实就是执行Boolan()方法。
# ==
1 == true // true
2 == true // false ---> Number(2) == Number(true) ---> 2 == 1
[1] == true // true
2
3
==,本质是执行Number()方法。
# Boolean()
| 类型 | 值 | Boolean() |
|---|---|---|
| undefined | undefined | false |
| null | null | false |
| string | '' | false |
| string | '0' | true |
| number | 0 | false |
| number | 1 | true |
| boolean | false | false |
| boolean | true | true |
| object | {} | true |
| object | {num:0} | true |
| object | [] | true |
| object | [0] | true |
总结:
- undefined、null都为false。
- 字符串只有''为false。
- 数值类型只有0为false。
- 引用数据类型都为true。
- 布尔类型看本身。
# Number()
| 类型 | 值 | Number() |
|---|---|---|
| undefined | undefined | NaN |
| null | null | 0 |
| string | '' | 0 |
| string | '0' | 0 |
| string | '1' | 1 |
| string | '1a' | NaN |
| number | 0 | 0 |
| number | 1 | 1 |
| boolean | false | 0 |
| boolean | true | 1 |
| object | {} | NaN |
| object | {num:0} | NaN |
| object | [] | 0 |
| object | [0] | 0 |
| object | [0,1] | NaN |
总结:
- undefined为NaN。
- null为0。
- 字符类型长度为0必为0。长度不为0看value是否包含非数字,不包含就是去掉引号后的值。否则NaN。
- 数值类型保持原值。
- 布尔类型true为1,false为0。
- 引用数据类型。
- 对象{}为NaN。
- 数组长度为0即为0。长度为1则转那一个数值,长度大于1则NaN。
# Math
max、min可以使用spread、rest或apply、call来改变传入参数是列表还是数组。
ceil、floorceil:天花板,floor:地板。
round四舍五入。
random取[0, num]的整数:
Math.floor(Math.random() * (num + 1))。取[num1, num2]的整数:本质还是[0 ,num]
function getRandom(num1, num2) { return ( Math.min(num1, num2) + Math.floor(Math.random() * (num2 - num1 + 1)) ); }1
2
3
4
5
# Date
# 获取时间戳
+dateNumber(date)date.valueOf()date.getTime
# 时间戳转ISO时间
new Date(timeStamp)
# 格式化
好库:moment.js
- 年:
getFullYear - 月:
getMonth,从0开始。 - 日:
getDate - 小时:
getHours - 分钟:
getMinutes - 秒:
getSeconds
const d = new Date("1999-11-10 03:03:12");
function formatDate(date, format) {
const config = {
YY: date.getFullYear(),
MM: date.getMonth(),
DD: date.getDate(),
HH: date.getHours(),
mm: date.getMinutes(),
SS: date.getSeconds(),
};
for (let item in config) {
format = format.replace(item, config[item]);
}
return format;
}
console.log(formatDate(d, "YY年MM月"));
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 原型[[prototype]]
# 含义
显式原型(常规属性):
prototypeprototype仅用在构造函数创建新对象new F()时,为新对象的[[prototype]]赋值。隐式原型:
__proto__用于设置原型
[[prototype]]的一种方式。
# 查找
Object.getPrototypeOf()
注意:更新显式原型时,一定要在产生实例之前更新,不然实例拿到的显式原型是以前的原型。
# constructor
Fn.prototype.constructor === Fn
# 继承
通过__proto__设置[[prototype]]。
# this指向问题
无论在哪里找到方法:在一个对象还是在原型中。在一个方法调用中,this 始终是点符号 . 前面的对象。
# 常用方法
# 设置原型
Object.setPropertyOf(o. proto)__proto__
# instanceof
instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
原理,__proto__一直向上找。
# isPrototypeOf
isPrototypeOf() 方法用于检查一个对象是否存在于另一个对象的原型链中。
# 创建指定原型的对象
Object.create()
# Class类
# new的本质
- 创建新对象
- 执行
constructor为对象添加属性。 - 为类的原型存储类的方法。
# Class的本质
function Class() {
this[key] = value
}
Class.prototype.method = ...
2
3
4
# constructor
初始化顺序:
类字段
- 对于基类(还未继承任何东西的那种),在构造函数调用前初始化。
- 对于派生类,在
super()后立刻初始化。
非类字段(往原型上添加的)
在构造函数调用前初始化。
# Class与function的区别
- 特殊属性
[[IsClassConstructor]],只能new - 类的方法是不可枚举的
enumerable - 类总是使用严格模式
- 类没有变量提升
- 类有两条继承链(构造函数+原型链)
# 类字段
“类字段”是一种允许添加任何属性的语法。
这种类似于在constructor里使用this[] = ...,会直接加在new出的对象里。而非类字段添加的方法,会添加到原型上。可以避免this指向出错的问题出现。
class User {
name = '123'
show = () => {}
}
2
3
4
# 继承extends
本质:子类prototype属性设置[[prototype]]为父类的prototype。
注意
- extends关键字后可以跟任意表达式,但原理仍是设置
[[prototype]]。 - 继承后,子类
[[prototype]]等于父类
# 重写
通俗一点,就是和父类的方法同名。此时,优先执行子类的方法。
优先顺序:类字段 > 类的原型方法 > 父类的原型方法
# super
构造方法
super()- 如果是派生类,未定义
constructor,默认执行父类的构造函数。 - 如果派生类已定义
constructor,必须在使用this之前使用super。因为,派生类中this来源于父类,只有基类是默认空对象赋给this。
- 如果是派生类,未定义
普通方法
super.method()- 原理:方法在内部的
[[HomeObject]]属性中记住了它们的类/对象,[[HomeObject]]不能被更改,所以这个绑定是永久的。因此,super能够解析到父类方法。当方法被复制时,[[HomeObject]]仍然未变!!! - 注意:是方法,不是类字段喔~
- 原理:方法在内部的
# static静态
# 基础
静态的用武之地:
- 静态方法
- 静态属性
本质在于为类添加属性和方法,而不是在原型链上添加属性和方法,当然,静态属性就不能被类生成的对象访问。只有通过类.方法或者类.属性的方式使用。
# 继承
继承有两个地方:
Son.__proto__ ==== FatherSon.prototype.__proto__ === Father.prototype
# 讨论
任何类的顶层原型都是Object,这和extends有什么区别?
区别就在于子类的原型[[prototype]]等于父类
# 扩展内建类
使用extends关键字扩展js原生类如Array、Object和String等等。
# 特殊静态getter Symbol.species
当在执行map、filter等方法时,可以设置执行的constructor,方便扩展的内建类使用原生方法返回的类仍然是内建类。
注意,String、Array并不算是继承了Object,只继承了prototype上的原型,但是类之间并没有继承原型。
# 类型判断
| 用于 | 返回值 | |
|---|---|---|
typeof | 原始数据类型 | string |
{}.toString.call() | 原始数据类型,内建对象,包含 Symbol.toStringTag 属性的对象 | string |
instanceof | 对象 | true/false |
typeof
幺蛾子多,对
null、[]、{}返回值都是Object。{}.toString.call()加强版
typeof,本质读取对象属性Symbol.toStringTag,可以自定义更改。/** * 判断对象类型 * 对于 number 类型,结果是 [object Number] * 对于 boolean 类型,结果是 [object Boolean] * 对于 null:[object Null] * 对于 undefined:[object Undefined] * 对于数组:[object Array] * @param {*} obj * @returns */ function judgeType(obj) { return {}.toString.call(obj); }1
2
3
4
5
6
7
8
9
10
11
12
13自定义修改示例:
const obj = { [Symbol.toStringTag]: "Peter", }; console.log(judgeType(obj)); // [object Peter]1
2
3
4
5instanceof本质是顺着原型链向上找,和
isPrototypeOf一样的。
# try-catch
class HytError extends Error {
constructor(mesage) {
super(mesage);
this.name = this.constructor.name;
}
}
/**
* 包装异常
*/
class ReadError extends HytError {
constructor(message, cause) {
super(message);
this.cause = cause;
}
}
/**
* 校验异常
*/
class ValidationError extends HytError {
constructor(property) {
super(`not property: ${property}`);
this.property = property;
}
}
/**
* 格式化异常
*/
class FormatError extends HytError {
constructor(data) {
super(`${data} format error`);
}
}
const errorClassArr = [ValidationError, FormatError];
/**
* 校验User
* @param {Object} data
*/
function validateUser(data) {
if (!data.name) {
throw new ValidationError("name");
}
if (!data.age) {
throw new ValidationError("age");
}
}
/**
* 格式化data
* @param {Object} data
* @returns
*/
function formatData(data) {
let res = {};
try {
res = JSON.parse(data);
} catch (error) {
throw new FormatError(data);
}
return res;
}
/**
* 主函数
*/
function fn() {
try {
let orgin = '{"name":1}';
const data = formatData(orgin);
validateUser(data);
} catch (error) {
errorClassArr.forEach((item) => {
if (error instanceof item) {
throw new ReadError(error.message, error);
} else {
console.error(error);
}
});
}
}
/**
* 入口
*/
function main() {
try {
fn();
} catch (error) {
if (error instanceof ReadError) {
console.error(error.cause);
} else {
console.log(error);
}
}
}
main();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# 注意
try...catch只能捕捉同步任务的异常。throw后,后续的代码将不再执行。
# 全局catch
window.onerror = function(message, url, line, col, error) {
// ...
};
2
3
# Promise
# 使用
const promise = new Promise(function(resolve, reject) {
// executor 立即执行
});
2
3
当
new Promise被创建,executor 会自动运行。当 executor 获得了结果,无论是早还是晚都没关系,它应该调用以下回调之一:
resolve(value)—— 如果任务成功完成并带有结果value。reject(error)—— 如果出现了 error,error即为 error 对象。
若不调用回调,状态一直是
pending。状态变化
执行了
resolve、reject后,不会再执行resolve、reject,而executor的代码依旧会正常执行。
# then、catch
promise.then(suc=>{},err=>{})
promise.catch(err=>{})
2
3
promise 的执行者(executor)和 promise 的处理程序周围有一个“隐式的 try..catch”。如果发生异常(reject)或同步错误(Error),它就会被捕获,并被视为 rejection 进行处理。
# 全局未处理的reject
如果一个 promise 的 error 未被在微任务队列的末尾进行处理,则会出现“未处理的 rejection”。
window.addEventListener('unhandledrejection', function(event) {
// 这个事件对象有两个特殊的属性:
alert(event.promise); // [object Promise] —— 生成该全局 error 的 promise
alert(event.reason); // Error: Whoops! —— 未处理的 error 对象
});
2
3
4
5
6
# 常用静态API
# Promise.all
常用于执行多个promise,并等待所有promise准备就绪。
Promise.all(iterable)
注意:
如果有多个同时进行的
fetch调用,其中一个失败,其他的fetch操作仍然会继续执行,但是Promise.all将不会再关心(watch)它们。它们可能会 settle,但是它们的结果将被忽略。
- 如果有一个
promise被reject,则Pormise.all会立即reject,并携带这个reject的error。 - 参数
iterable是可迭代对象,一般是数组。数组里的元素,一般是Pormise对象,但是允许是非Promsie对象。
# Promise.allSettled
和Promise.all的API类似,不同的是allSettled的结果里既有fullfiled又有rejected。我们仍然可以对fullfilled状态的结果进行处理。
# Promise.race
返回第一个settled的promise。
# Promise.any
返回第一个fullfiled的promise。
# Promise.resolve/reject
如名。
# 微任务队列(Microtask queue)
当一个 promise 准备就绪时,它的 .then/catch/finally 处理程序就会被放入队列中:但是它们不会立即被执行。当 JavaScript 引擎执行完当前的代码,它会从队列中获取任务并执行它。
# async/await
# async function
有async关键字开始的函数,默认返回promise对象,其他值将被包装在一个 resolved 的 promise 中。
# await
等待promise完成settle。不仅支持等待promise,还支持thenable。即包含then(resolve,reject)函数的对象。
注意:如果 await 接收了一个非 promise 的但是提供了 .then 方法的对象,它就会调用这个 .then 方法,并将内建的函数 resolve 和 reject 作为参数传入(就像它对待一个常规的 Promise executor 时一样)。然后 await 等待被调用(在上面这个例子中发生在 (*) 行),然后使用得到的结果继续执行后续任务。
# Error
针对await中返回rejected状态的promise对象,本质相当于throw...,因此只需try...catch。
async function fn(){
await Promise.reject(...)
}
2
3
优雅解决方案:
fn().catch(...)fn内部处理。
# 中文转码
- 转URL安全格式
encodeURIComponent - 解码
decodeURIComponent