ES2015~ES2022温旧知新
解构赋值
/* 解构赋值 */ let { max, min } = Math let { length: len } = 'hello world'
标签模板
/* 标签模板 */ // eg.1 alert`hello world` // 等同于 alert(['hello']) // eg.2 function jsx(strArr, ...valArr) { let outputStr = '' let i = 0 for (; i < valArr.length; i++) { outputStr += strArr[i] + `<<${valArr[i]}>>` } outputStr += strArr[i] return outputStr } jsx`hello ${23} world` // 'hello <<23>> world'
字符串方法
String.prototype.fn
/* 字符串方法 */ // 包含 includes() // 以开始 startsWith() // 以结束 endsWith() // 重复N次 repeat(n) // 前补齐 padStart(len, str) // 后补齐 padEnd(len, str) // 去前空格 trimStart() // 去尾空格 trimEnd() // 替换全部 replaceAll(str, newStr) // 支持负索引的查找指定位置字符 at(index)
数值
/* 数值 */ // ES2021,可用_做为数值分隔符 const num = 10_000 // 判断整数 Number.isInteger(5) /* Math 方法 */ // 去除小数部分(支持负数) Math.trunc(2.5) /* BigInt 数据类型 ES2020 */ // 以n结尾表示 const big = 1000n
函数
/* 函数 */ // try...catch... 允许省略参数 try { } catch { }
数组
/* 数组 */ // 将array-like 与 iterable对象转为数组 Array.from(iterable) // eg.1 iterator接口 Array.from({ length: 2 }) // [undefined, undefined] // eg.2 带处理函数 Array.from(document.querySelectorAll('div'), div => div.textContent) // 将一组值转为数组 Array.of(1, 2, 3) // Array.prototype // 将数组指定位置成员覆盖到其它位置,返回当前数组 copyWithin(target, start = 0, end = this.length) // 找到成员所在索引 findIndex() // eg.1 [100, 200, 300, 400].findIndex((value, index, arr) => value === 200) // 使用给定值填充数组 fill(val) // eg.1 ['l', 'e', 'n'].fill('t') // ['t', 't', 't'] // eg.2 new Array(5).fill('o') // ['o', 'o', 'o', 'o', 'o'] // key, value keys() values() entries() // 将数组拉平, n为层级数 flat(n = 1) // eg.1 [1, 2, 3, [4, 5]].flat() // [1, 2, 3, 4, 5] // 值行map,再用返回值执行flat flatMap(x => x * x) // 支持负索引引用 at(index)
对象
/* 对象方法 */ // 判断严格相等,于===不同之处只有两个:一是+0不等于-0,二是NaN等于自身 Object.is(arg1, arg2) // Object.entries() 的逆操作 Object.fromEntries() // eg.1 Object.fromEntries([['name', 'lenton'], ['age', 30]]) // {name: 'lenton', age: 30}
运算符
/* 运算符 */ // 指数运算符 5 ** 2 // 25 // 链判断运算符 左侧的对象是否为null或undefined。如果是的,就不再往下运算,而是返回undefined const name = res.data?.user?.name // Null 判断运算符 ES2020 运算符左侧的值为null或undefined时,返回右侧的值 const name = user.name ?? 'no name' // 逻辑赋值运算符 x ||= y // x || (x = y) x &&= y // x && (x = y) x ??= y // x ?? (x = y) // eg.1 // 等价于 x ?? 'default value' // 等价于(x ===undefined || x === null) || 'default value' x ??= 'default value'
Symbol
表示独一无二的值
// eg.1 const symbol = Symbol() // eg.2 可以接受一个字符串作为参数,表示对 Symbol 实例的描述,便于区分 const symbol2 = Symbol('id') symbol2.description // Symbol(id) console.log(symbol2) // Symbol(id) // 搜索以该参数为名称的 Symbol 值。有则返回这个 Symbol 值,否则就新建一个以该字符串为名称的 Symbol 值,并将其注册到全局 Symbol.for(name) // 返回一个已登记的 Symbol 类型值的key Symbol.keyFor(symbol) // eg.1 let s1 = Symbol.for('foo'); Symbol.keyFor(s1) // 'foo'
Set, Map
1. Set
// 去重 const newArr = [...new Set([1, 1, 2])] const newStr = [...new Set('lenton')].join('')
Set.prototype
size // 成员总数 add(value) delete(value) has(value) clear() keys() values() entries() forEach()
2. Map
Map.prototype
size // 成员总数 set(key, value) get(key) has(key) delete(key) clear() keys() values() entries() forEach()
Proxy
const student = new Proxy({}, { get(target, key) { return 'Name is ' + (target[key] ?? '') }, set(target, key, value) { if (key !== 'name') { throw Error(`兄弟! 你不能修改 ${key}`) } target[key] = value } })
Reflect
ES6 为了操作对象而提供的新 API
1. 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Reflect对象上
2.修改某些Object方法的返回结果,让其变得更合理。
3.让Object操作都变成函数行为。(某些Object操作是命令式)
// 传统 'keys' in Object // true // 新写法 Reflect.has(Object, 'keys') // true
4. Reflect对象的方法与Proxy对象的方法一一对应,只要是Proxy对象的方法,就能在Reflect对象上找到对应的方法。
const proxy = new Proxy({}, { get(target, name) { console.log('这是在默认行为前,加入的其它行为') return Reflect.get(target, name) }, has(target, name) { console.log('这是在默认行为前,加入的其它行为') return Reflect.has(target, name) } })
静态方法
Reflect对象一共有 13 个静态方法。
Reflect.apply(target, thisArg, args) Reflect.construct(target, args) Reflect.get(target, name, receiver) Reflect.set(target, name, value, receiver) Reflect.defineProperty(target, name, desc) Reflect.deleteProperty(target, name) Reflect.has(target, name) Reflect.ownKeys(target) Reflect.isExtensible(target) Reflect.preventExtensions(target) Reflect.getOwnPropertyDescriptor(target, name) Reflect.getPrototypeOf(target) Reflect.setPrototypeOf(target, prototype)
删除属性
const obj = { name: 'lenton' } Reflect.deleteProperty(obj, 'name')
Promise
Promise.prototype.finally() 用于指定不管 Promise 对象最后状态如何,都会执行的操作 (ES2018)
promise.then().catch().finally()
在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race() 方法的参数与Promise.all()方法一样,如果不是 Promise 实例,就会先调用下面讲到的Promise.resolve()方法,将参数转为 Promise 实例,再进一步处理。
const p = Promise.race([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
Promise.allSettled() 方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
const promises = [ fetch('/api-1'), fetch('/api-2'), fetch('/api-3'), ]; await Promise.allSettled(promises);
该方法返回的新的 Promise 实例,一旦发生状态变更,状态总是fulfilled,不会变成rejected。状态变成fulfilled后,它的回调函数会接收到一个数组作为参数,该数组的每个成员对应前面数组的每个 Promise 对象。
在回调参数status属性中反应各个状态
// 异步操作成功时 {status: 'fulfilled', value: value} // 异步操作失败时 {status: 'rejected', reason: reason}
例子
let p1 = new Promise((resolve, reject) => { setTimeout(() => { reject(11) }, 1000) }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve(22) }, 500) }) Promise.allSettled([p1, p2]).then(res => { console.log(res) }) // [{"status":"rejected","reason":11},{"status":"fulfilled","value":22}]
Promise.any() (ES2021)方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回
只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态
let p1 = new Promise((resolve, reject) => { setTimeout(() => { reject(11) }, 1000) }) let p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve(22) }, 1500) }) Promise.any([p1, p2]).then(res => { console.log(res) }).catch(err => { console.log(err) })
Promise.resolve() 将现有对象转为 Promise 对象
const p = Promise.resolve(100) p.then(res => { console.log(res) }) // 100
Promise.reject() 将现有对象转为 Promise 对象(同上,只是状态相反为 reject)
Promise.try() 事实上,Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。
Promise.try(() => apiget({id: userId})) .then(...) .catch(...)
Iterator
它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员
Iterator 接口主要供for...of消费
Iterator 的遍历过程是这样的:
(1)创建一个指针对象,指向当前数据结构的起始位置。也就是说,遍历器对象本质上,就是一个指针对象。
(2)第一次调用指针对象的next
方法,可以将指针指向数据结构的第一个成员。
(3)第二次调用指针对象的next
方法,指针就指向数据结构的第二个成员。
(4)不断调用指针对象的next
方法,直到它指向数据结构的结束位置。
每一次调用next
方法,都会返回数据结构的当前成员的信息。具体来说,就是返回一个包含value
和done
两个属性的对象。其中,value
属性是当前成员的值,done
属性是一个布尔值,表示遍历是否结束
let arr = [1, 2, 3] let iter = arr[Symbol.iterator]() iter.next().value // 1 iter.next().value // 2
模拟next方法返回值的例子
function makeIterator(array) { var nextIndex = 0; return { next: function() { return nextIndex < array.length ? {value: array[nextIndex++]} : {done: true}; } };}
默认 Iterator 接口
ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”
原生实现Iterator接口的数据类型有:
- Array
- Map
- Set
- String
- NodeList
- 函数的 arguments 对象
部署 Iterator 接口
const iterator = { [Symbol.iterator]() { let nextIndex = 0 return { next() { return nextIndex < 5 ? { value: nextIndex++ } : { done: true } } } } } for (let item of iterator) { console.log(item) }
遍历器对象的 return(),throw()
遍历器对象生成函数,next()方法是必须部署的,return()方法和throw()方法是否部署是可选的
return() 方法的使用场合是,如果for...of循环提前退出(通常是因为出错,或者有break语句),就会调用return()方法
throw() 方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法
for...of 循环
一个数据结构只要部署了Symbol.iterator属性,就被视为具有 iterator 接口,就可以用for...of循环遍历它的成员
Generator
Generator 函数是 ES6 提供的一种异步编程解决方案
未掌握,待补充
Async / await
并发执行
let [foo, bar] = await Promise.all([getFoo(), getBar()])
async 函数的实现原理 待补充
Class
取值函数(getter)和存值函数(setter)
class MyClass { constructor() { // ... } get prop() { return 'getter'; } set prop(value) { console.log('setter: '+value); } } let inst = new MyClass(); inst.prop = 123; // setter: 123 inst.prop // 'getter'
this 指针
类的方法内,this 指向类的实例, 静态方法 static 中, this 指向类本身
静态块
ES2022 引入了静态块(static block),允许在类的内部设置一个代码块,在类生成时运行一次,主要作用是对静态属性进行初始化
class C { static x = ...; static y; static z; static { try { const obj = doSomethingWith(this.x); this.y = obj.y; this.z = obj.z; } catch { this.y = ...; this.z = ...; } } }
super 关键字
1) super作为函数调用时,代表父类的构造函数。子类的构造函数必须执行一次super函数
class A {} class B extends A { constructor() { super(); } }
2) super作为对象时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类
class A { constructor() { this.p = 2; } } class B extends A { get m() { return super.p; } } let b = new B(); b.m // undefined
参与讨论