因为篇幅限制,因此将文章分红了 2 篇,这是下篇,包含了从 ES2016
到 ES2021
的全部知识。html
判断一个数组是否包含某个元素,以前通常是这么作的:vue
if (arr.indexOf(el) >= 0) {}
// 或者
if (~arr.indexOf(el)) {}
复制代码
而如今你能够这么作了:webpack
if (arr.includes(el)) {}
复制代码
indexOf
会返回找到元素在数组中的索引位置,判断的逻辑是是否严格相等,因此他在遇到 NaN
的时候不能正确返回索引,可是 includes
解决了这个问题:git
[1, NaN, 3].indexOf(NaN) // -1
[1, NaN, 3].includes(NaN) // true
复制代码
x ** y
是求 x
的 y
次幂,和 Math.pow(x, y)
功能一致:es6
// x ** y
let squared = 2 ** 2 // 2 * 2 = 4
let cubed = 2 ** 3 // 2 * 2 * 2 = 8
复制代码
x **= y
表示求 x
的 y
次幂,而且把结果赋值给 x
:github
// x **= y
let x = 2;
x **= 3 // x 最后等于 8
复制代码
返回一个由对象自身全部可遍历属性的属性值组成的数组:web
const person = { name: '布兰' };
Object.defineProperty(person, 'age', {
value: 12,
enumrable: false // age 属性将不可遍历
})
console.log(Object.values(person)) // ['布兰']
// 相似 str.split('') 效果
console.log(Object.values('abc')) // ['a', 'b', 'c']
复制代码
返回一个由对象自身全部可遍历属性的键值对组成的数组:正则表达式
const person = { name: '布兰', age: 12 }
console.log(Object.entries(person)) // [["name", "布兰"], ["age", 12]]
复制代码
利用这个方法能够很好的将对象转成正在的 Map
结构:vue-cli
const person = { name: '布兰', age: 12 }
const map = new Map(Object.entries(person))
console.log(map) // Map { name: '布兰', age: 12 }
复制代码
Object.getOwnPropertyDescriptor()
会返回指定对象某个自身属性的的描述对象,而 Object.getOwnPropertyDescriptors()
则是返回指定对象自身全部属性的描述对象:npm
const person = { name: '布兰', age: 12 }
console.log(Object.getOwnPropertyDescriptor(person, 'name'))
// { configurable: true, enumerable: true, value: "布兰", writable: true }
console.log(Object.getOwnPropertyDescriptors(person))
//{
// name: { configurable: true, enumerable: true, value: "布兰", writable: true },
// age: {configurable: false, enumerable: false, value: 12, writable: false}
//}
复制代码
配合 Object.create()
能够实现浅克隆:
const shallowClone = (obj) => Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
)
复制代码
str.padStart(length [, padStr])
会返回一个新字符串,该字符串将从 str
字符串的左侧开始填充某个字符串 padStr
(非必填,若是不是字符串则会转成字符串, 传入 undefined
和不传这个参数效果一致)直到达到指定位数 length
为止:
'abc'.padStart(5, 2) // '22abc'
'abc'.padStart(5, undefined) // ' abc'
'abc'.padStart(5, {}) // '[oabc'
'abc'.padStart(5) // ' abc'
'abcde'.padStart(2, 'f') // 'abcde'
复制代码
规则和 padStart
相似,可是是从字符串右侧开始填充:
'abc'.padEnd(5, 2) // 'abc22'
复制代码
容许函数在定义和调用的时候时候最后一个参数后加上逗号:
function init( param1, param2, ) { }
init('a', 'b',)
复制代码
使用 async
能够声明一个 async
函数,结合 await
能够用一种很简介的方法写成基于 Promise
的异步行为,而不须要刻意的链式调用。await
表达式会暂停整个 async
函数的执行进程并出让其控制权,只有当其等待的基于 Promise
的异步操做被兑现或被拒绝以后才会恢复进程。async
函数有以下几种定义形式:
// 函数声明
async function foo() {}
// 函数表达式
let foo = async function() {}
// 箭头函数
let foo = async () => {}
// 对象方法
lef obj = {
async foo() {}
}
// 类方法
class Dog {
async bark() {}
}
复制代码
async
函数必定会返回一个 Promise
对象,因此它可使用 then
添加处理函数。若是一个 async
函数的返回值看起来不是Promise
,那么它将会被隐式地包装在一个 Promise
中:
async function foo() {
return 'a'
}
foo().then(res => {
console.log(res) // 'a'
})
复制代码
内部若是发生错误,或者显示抛出错误,那么 async
函数会返回一个 rejected
状态的 Promsie
:
async function foo() {
throw new Error('error')
}
foo().catch(err => {
console.log(err) // Error: error
})
复制代码
返回的 Promise
对象必须等到内部全部 await
命令 Promise
对象执行完才会发生状态改变,除非遇到 return
语句或抛出错误;任何一个 await
命令返回的 Promise
对象变 为rejected
状态,整个 Async
函数都会中断后续执行:
async function fn() {
let a = await Promise.resolve('success')
console.log('a_' + a)
let b = await Promise.reject('fail')
console.log('b_' + b) // 不会执行
}
fn().then(res => {
console.log(res) // 不会执行
}, err => {
console.log(err)
})
// 'a_success'
// 'fail'
复制代码
因此为了保证 async
里的异步操做都能完成,咱们须要将他们放到 try...catch()
块里或者在 await
返回的 Promise
后跟一个 catch
处理函数:
async function fn() {
try {
let a = await Promise.reject('a fail')
console.log('a_' + a) // 不会执行
} catch (e) {
console.log(e) // 'a fail'
}
let b = await Promise.reject('b fail')
.catch(e => {
console.log(e) // 'b fail'
})
console.log('b_' + b) // 'bundefined'
}
fn().then(res => {
console.log(res) // undefined
}, err => {
console.log(err) // 不会执行
})
复制代码
若是 async
函数里的多个异步操做之间没有依赖关系,建议将他们写到一块儿减小执行时间:
// 写法一
let [foo, bar] = await Promise.all([getFoo(), getBar()])
// 写法二
let fooPromise = getFoo()
let barPromise = getBar()
let foo = await fooPromise
let bar = await barPromise
复制代码
await
命令只能用在 async
函数之中,若是用在普通函数,就会报错。
Promise.prototype.finally()
用于给 Promise
对象添加 onFinally
函数,这个函数主要是作一些清理的工做,只有状态变化的时候才会执行该 onFinally
函数。
function onFinally() {
console.log(888) // 并不会执行
}
new Promise((resolve, reject) => {
}).finally(onFinally)
复制代码
finally()
会生成一个 Promise
新实例,finally 通常会原样后传父 Promise,不管父级实例是什么状态:
let p1 = new Promise(() => {})
let p2 = p1.finally(() => {})
setTimeout(console.log, 0, p2) // Promise {<pending>}
let p3 = new Promise((resolve, reject) => {
resolve(3)
})
let p4 = p3.finally(() => {})
setTimeout(console.log, 0, p3) // Promise {<fulfilled>: 3}
复制代码
上面说的是通常,可是也有特殊状况,好比 finally
里返回了一个非 fulfilled
的 Promise
或者抛出了异常的时候,则会返回对应状态的新实例:
let p1 = new Promise((resolve, reject) => {
resolve(3)
})
let p2 = p1.finally(() => new Promise(() => {}))
setTimeout(console.log, 0, p2) // Promise {<pending>}
let p3 = p1.finally(() => Promise.reject(6))
setTimeout(console.log, 0, p3) // Promise {<rejected>: 6}
let p4 = p1.finally(() => {
throw new Error('error')
})
setTimeout(console.log, 0, p4) // Promise {<rejected>: Error: error}
复制代码
参考:深刻理解Promise
想要了解异步迭代器最好的方式就是和同步迭代器进行对比。咱们知道可迭代数据的内部都是有一个 Symbol.iterator
属性,它是一个函数,执行后会返回一个迭代器对象,这个迭代器对象有一个 next()
方法能够对数据进行迭代,next()
执行后会返回一个对象,包含了当前迭代值 value
和 标识是否完成迭代的 done
属性:
let iterator = [1, 2][Symbol.iterator]()
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: undefinde, done: true }
复制代码
上面这里的 next()
执行的是同步操做,因此这个是同步迭代器,可是若是 next()
里须要执行异步操做,那就须要异步迭代了,可异步迭代数据的内部有一个 Symbol.asyncIterator
属性,基于此咱们来实现一个异步迭代器:
class Emitter {
constructor(iterable) {
this.data = iterable
}
[Symbol.asyncIterator]() {
let length = this.data.length,
index = 0;
return {
next:() => {
const done = index >= length
const value = !done ? this.data[index++] : undefined
return new Promise((resolve, reject) => {
resolve({value, done})
})
}
}
}
}
复制代码
异步迭代器的 next()
会进行异步的操做,一般是返回一个 Promise
,因此须要对应的处理函数去处理结果:
let emitter = new Emitter([1, 2, 3])
let asyncIterator = emitter[Symbol.asyncIterator]()
asyncIterator.next().then(res => {
console.log(res) // { value: 1, done: false }
})
asyncIterator.next().then(res => {
console.log(res) // { value: 2, done: false }
})
asyncIterator.next().then(res => {
console.log(res) // { value: 3, done: false }
})
复制代码
另外也可使用 for await...of
来迭代异步可迭代数据:
let asyncIterable = new Emitter([1, 2, 3])
async function asyncCount() {
for await (const x of asyncIterable ) {
console.log(x)
}
}
asyncCount()
// 1 2 3
复制代码
另外还能够经过异步生成器来建立异步迭代器:
class Emitter {
constructor(iterable) {
this.data = iterable
}
async *[Symbol.asyncIterator]() {
let length = this.data.length,
index = 0;
while (index < length) {
yield this.data[index++]
}
}
}
async function asyncCount() {
let emitter = new Emitter([1, 2, 3])
const asyncIterable = emitter[Symbol.asyncIterator]()
for await (const x of asyncIterable ) {
console.log(x)
}
}
asyncCount()
// 1 2 3
复制代码
参考:
正则表达式新增了一个 s
修饰符,使得 .
能够匹配任意单个字符:
/foo.bar/.test('foo\nbar') // false
/foo.bar/s.test('foo\nbar') // true
复制代码
上面这又被称为 dotAll
模式,表示点(dot
)表明一切字符。因此,正则表达式还引入了一个dotAll属性,返回一个布尔值,表示该正则表达式是否处在dotAll模式:
/foo.bar/s.dotAll // true
复制代码
正则表达式可使用捕获组来匹配字符串,可是想要获取某个组的结果只能经过对应的索引来获取:
let re = /(\d{4})-(\d{2})-(\d{2})/
let result = re.exec('2015-01-02')
// result[0] === '2015-01-02'
// result[1] === '2015'
// result[2] === '01'
// result[3] === '02'
复制代码
而如今咱们能够经过给捕获组 (?<name>...)
加上名字 name
,经过名字来获取对应组的结果:
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
let result = re.exec('2015-01-02')
// result.groups.year === '2015'
// result.groups.month === '01'
// result.groups.day === '02'
复制代码
配合解构赋值能够写出很是精简的代码:
let {groups: {year, month, day}} = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/.exec('2015-01-02')
console.log(year, month, day) // 2015 01 02
复制代码
具名组也能够经过传递给 String.prototype.replace
的替换值中进行引用。若是该值为字符串,则可使用 $<name>
获取到对应组的结果:
let re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
let result = '2015-01-02'.replace(re, '$<day>/$<month>/$<year>')
// result === '02/01/2015'
复制代码
参考:proposal-regexp-named-groups
后行断言: (?<=y)x
,x
只有在 y
后面才能匹配:
/(?<=\$)\d+/.exec('I have $100.') // ['100'] 复制代码
后行否认断言: (?<!y)x
,x
只有不在 y
后面才能匹配:
/(?<!\$)\d+/.exec('I have $100.') // ['00'] 复制代码
容许正则表达式匹配符合 Unicode
某种属性的全部字符,\p{...}
是匹配包含,\P{...}
是匹配不包含的字符,且必须搭配 /u
修饰符才会生效:
/\p{Emoji}+/u.exec('😁😭笑死我了🤣😂不行了') // ['😁😭'] /\P{Emoji}+/u.exec('😁😭笑死我了🤣😂不行了') // ['笑死我了'] 复制代码
这里能够查询到更多的 Unicode
的属性 Full_Properties
对象的扩展运算符能够用到解构赋值上,且只能应用到最后一个变量上:
let {x, ...y} = {x: 1, a: 2, b: 3}
console.log(y) // {a: 2, b: 3}
复制代码
对象扩展运算符不能解构原型上的属性:
let obj = { x: 1 }
obj.__proto__ = { y: 2 }
let {...a} = obj
console.log(a.y) // undefined
复制代码
应用一:能够实现浅拷贝,可是不会拷贝原始属性:
let person = Object.create({ name: '布兰' })
person.age = 12
// 浅拷贝写法一
let { ...pClone1 } = person
console.log(pClone1) // { age: 12 }
console.log(pClone1.name) // undefined
// 浅拷贝写法二
let pClone2 = {...person}
console.log(pClone2) // { age: 12 }
console.log(pClone2.name) // undefined
复制代码
应用二:合并两个对象:
let ab = {...a, ...b}
// 等同于
let ab = Object.assign({}, a, b);
复制代码
应用三:重写对象属性
let aWithOverrides = { ...a, x: 1, y: 2 };
复制代码
应用四:给新对象设置默认值
let aWithDefaults = { x: 1, y: 2, ...a };
复制代码
应用五:利用扩展运算符的解构赋值能够扩展函数参数:
function baseFunction({ a, b }) {}
function wrapperFunction({ x, y, ...restConfig }) {
// 使用 x 和 y 参数进行操做
// 其他参数传给原始函数
return baseFunction(restConfig)
}
复制代码
参考:
异常被捕获的时候若是不须要作操做,甚至能够省略 catch(err)
里的参数和圆括号:
try {
} catch {
}
复制代码
UTF-8
标准规定,0xD800
到 0xDFFF
之间的码点,不能单独使用,必须配对使用。 因此 JSON.stringify()
对单个码点进行操做,若是码点符合 UTF-8
标准,则会返回对应的字符,不然会返回对应的码点:
JSON.stringify('\u{1f600}') // ""😀""
JSON.stringify('\u{D834}') // ""\ud834""
复制代码
Symbol
实例新增了一个描述属性 description
:
let symbol = Symbol('foo')
symbol.description // 'foo'
复制代码
函数的 toString()
会原样输出函数定义时候的样子,不会省略注释和空格。
Object.fromEntries()
方法是 Object.entries()
的逆操做,用于将一个键值对数组转为对象:
let person = { name: '布兰', age: 12 }
let keyValueArr = Object.entries(person) // [['name', '布兰'], ['age', 12]]
let obj = Object.fromEntries(arr) // { name: '布兰', age: 12 }
复制代码
经常使用可迭代数据结构之间的装换:
let person = { name: '布兰', age: 12 }
// 对象 -> 键值对数组
let keyValueArr = Object.entries(person) // [['name', '布兰'], ['age', 12]]
// 键值对数组 -> Map
let map = new Map(keyValueArr) // Map {"name": "布兰", "age": 12}
// Map -> 键值对数组
let arr = Array.from(map) // [['name', '布兰'], ['age', 12]]
// 键值对数组 -> 对象
let obj = Array.from(arr).reduce((acc, [ key, val ]) => Object.assign(acc, { [key]: val }), {}) // { name: '布兰', age: 12 }
复制代码
JavaScript
规定有 5 个字符,不能在字符串里面直接使用,只能使用转义形式。
U+005C
:反斜杠(reverse solidus)U+000D
:回车(carriage return)U+2028
:行分隔符(line separator)U+2029
:段分隔符(paragraph separator)U+000A
:换行符(line feed)可是因为 JSON
容许字符串里可使用 U+2028
和 U+2029
,因此使得 JSON.parse()
去解析字符串的时候可能会报错,因此 ES2019
容许模板字符串里能够直接这两个字符:
JSON.parse('"\u2028"') // ""
JSON.parse('"\u2029"') // ""
JSON.parse('"\u005C"') // SyntaxError
复制代码
消除字符串头部空格,返回一个新字符串;浏览器还额外增长了它的别名函数 trimLeft()
:
let str = ' hello world '
let newStr = str.trimStart()
console.log(newStr, newStr === str)
// 'hello world ' false
复制代码
消除字符串尾部空格,返回一个新字符串;浏览器还额外增长了它的别名函数 trimRight()
:
let str = ' hello world '
let newStr = str.trimEnd()
console.log(newStr, newStr === str)
// ' hello world' false
复制代码
arr.flat(depth)
按照 depth
(不传值的话默认是 1)深度拍平一个数组,而且将结果以新数组形式返回:
// depth 默认是 1
const arr1 = [1, 2, [3, 4]]
console.log(arr1.flat()) // [1, 2, 3, 4]
// 使用 Infinity,可展开任意深度的嵌套数组;自动跳过空数组;
const arr2 = [1, , [2, [3, [4]]]]
console.log(arr2.flat(Infinity))
// [1, 2, 3, 4]
复制代码
用 reduce
实现拍平一层数组:
const arr = [1, 2, [3, 4]]
// 方法一
let newStr = arr.reduce((acc, cur) => acc.concat(cur), [])
// 方法二
const flattened = arr => [].concat(...arr)
flattened(arr)
复制代码
参考:flat
flatMap(callback)
使用映射函数 callback
映射每一个元素,callback
每次的返回值组成一个数组,而且将这个数组执行相似 arr.flat(1)
的操做进行拍平一层后最后返回结果:
const arr1 = [1, 2, 3, 4]
arr1.flatMap(x => [x * 2])
// 将 [[2], [4], [6], [8]] 数组拍平一层获得最终结果:[2, 4, 6, 8]
复制代码
参考:flatMap
String.prototype.matchAll()
方法,能够一次性取出全部匹配。不过,它返回的是一个 RegExpStringIterator
迭代器同是也是一个可迭代的数据结构,因此能够经过 for...of
进行迭代:
let str = 'test1test2'
let regexp = /t(e)(st(\d?))/g
let iterable = str.matchAll(regexp)
for (const x of iterable) {
console.log(x)
}
// ['test1', 'e', 'st1', '1', index: 0, input: 'test1test1', groups: undefined]
// ['test2', 'e', 'st2', '2', index: 5, input: 'test1test2', groups: undefined]
复制代码
注意当使用 matchAll(regexp) 的时候,正则表达式必须加上 /g 修饰符。
也能够将这个可迭代数据转成数组形式:
// 方法一
[...str.matchAll(regexp)]
// 方法二
Array.from(str.matchAll(regexp))
复制代码
标准用法的 import
导入的模块是静态的,会使全部被导入的模块,在加载时就被编译(没法作到按需编译,下降首页加载速度)。有些场景中,你可能但愿根据条件导入模块或者按需导入模块,这时你可使用动态导入代替静态导入。
好比按需加载一个模块能够这样:
if (xxx) {
import('./module.js')
}
复制代码
import()
是异步导入的,结果会返回一个 Promise
:
import('/module.js')
.then((module) => {
// Do something with the module.
})
复制代码
动态 import()
的应用场景挺多的,好比 Vue
中的路由懒加载就是使用的动态导入组件。另外因为动态性不便于静态分析工具和 tree-shaking
工做,因此不能滥用。
BigInt
是一种内置对象,它提供了一种方法来表示大于
- 1 的整数。这本来是 Javascript
中能够用 Number
表示的最大数字。BigInt
能够表示任意大的整数。
为了区分 Number
,定义一个 BigInt
须要在整数后面加上一个 n
,或者用函数直接定义:
const num1 = 10n
const num2 = BigInt(20)
复制代码
Number
和 BigInt
之间能进行比较,但他们之间是宽松相等;且因为他们表示的是不一样类型的数字,因此不能直接进行四则运算:
10n == 10 // true
10n === 10 // false
10n > 8 // true
10 + Number(10n) // 20
10 + 10n // TypeError
复制代码
Promise.allSettled(iterable)
当全部的实例都已经 settled
,即状态变化过了,那么将返回一个新实例,该新实例的内部值是由全部实例的值和状态组合成的数组,数组的每项是由每一个实例的状态和内部值组成的对象。
function init(){
return 3
}
let p1 = Promise.allSettled([
new Promise((resolve, reject) => {
resolve(9)
}).then(res => {}),
new Promise((resolve, reject) => {
reject(6)
}),
init()
])
let p2 = p1.then(res => {
console.log(res)
}, err => {
console.log(err)
})
// [
// {status: "fulfilled", value: undefined},
// {status: "rejected", reason: 6},
// {status: "fulfilled", value: 3}
// ]
复制代码
只要全部实例中包含一个 pending
状态的实例,那么 Promise.allSettled()
的结果为返回一个这样 Promise {<pending>}
的实例。
在之前,从不一样的 JavaScript
环境中获取全局对象须要不一样的语句。在 Web
中,能够经过 window
、self
或者 frames
取到全局对象,可是在 Web Workers
中,只有 self
能够。在 Node.js
中,它们都没法获取,必须使用 global
。
而如今只须要使用 globalThis
便可获取到顶层对象,而不用担忧环境问题。
// 在浏览器中
globalThis === window // true
复制代码
import.meta
是一个给 JavaScript
模块暴露特定上下文的元数据属性的对象。它包含了这个模块的信息,好比说这个模块的 URL
,import.meta
必须在一个模块里使用:
// 没有声明 type="module",就使用 import.meta 会报错
<script type="module" src="./js/module.js"></script>
// 在module.js里
console.log(import.meta)
// {url: "http://localhost/3ag/js/module.js"}
复制代码
若是须要在配置了 Webpack
的项目,好比 Vue
里使用 import.meta
须要加一个包且配置一下参数,不然项目编译阶段会报错。
包配置详情参考:@open-wc/webpack-import-meta-loader
好比我用的是 4.x
版本的 vue-cli
,那我须要在 vue.config.js
里配置:
module.exports = {
chainWebpack: config => {
config.module
.rule('js')
.test(/\.js$/)
.use('@open-wc/webpack-import-meta-loader')
.loader('@open-wc/webpack-import-meta-loader')
.end()
}
}
复制代码
一般咱们获取一个深层对象的属性会须要写不少判断或者使用逻辑与 &&
操做符,由于对象的某个属性若是为 null
或者 undefined
就有可能报错:
let obj = {
first: {
second: '布兰'
}
}
// 写法一
let name1 = ''
if (obj) {
if (obj.first) {
name1 = obj.first.second
}
}
// 写法二
let name2 = obj && obj.first && obj.first.second
复制代码
?.
操做符容许读取位于链接对象链深处的属性的值,而没必要明确验证链中的每一个引用是否有效。若是某个属性为 null
或者 undefined
则结果直接为 undefined
。有了可选链操做符就可使得表达式更加简明了,对于上面例子用可选链操做符能够这么写:
let name3 = obj?.first?.second
复制代码
对于逻辑或 ||
运算符,当对运算符左侧的操做数进行装换为 Boolean
值的时候,若是为 true
,则取左边的操做数为结果,不然取右边的操做数为结果:
let name = '' || '布兰'
console.log(name) // '布兰'
复制代码
咱们都知道 ''
、0
、null
、undefined
、false
、NaN
等转成 Boolean
值的时候都是 false
,因此都会取右边的操做数。这个时候若是要给变量设置默认值,若是遇到自己值就多是 ''
或 0
的状况那就会出错了,会被错误的设置为默认值了。
而 ??
操做符就是为了解决这个问题而出现的,x ?? y
只有左侧的操做数为 null
或 undefined
的时候才取右侧操做数,不然取左侧操做数:
let num = 0 ?? 1
console.log(num) // 0
复制代码
以下这几个提案已经肯定了会在 2021 年发布,因此把他们归到 ES2021
中。
以前须要替换一个字符串里的所有匹配字符能够这样作:
const queryString = 'q=query+string+parameters'
// 方法一
const withSpaces1 = queryString.replace(/\+/g, ' ')
// 方法二
const withSpaces2 = queryString.split('+').join(' ')
复制代码
而如今只须要这么作:
const withSpace3 = queryString.replaceAll('+', ' ')
复制代码
replaceAll 的第一个参数能够是字符串也能够是正则表达式,当是正则表达式的时候,必须加上全局修饰符 /g,不然报错。
Promsie.any()
和 Promise.all()
同样接受一个可迭代的对象,而后依据不一样的入参会返回不一样的新实例:
传一个空的可迭代对象或者可迭代对象全部 Promise
都是 rejected
状态的,则会抛出一个 AggregateError
类型的错误,同时返回一个 rejected
状态的新实例:
let p1 = Promise.any([])
let p2.catch(err => {})
setTimeout(console.log, 0, p1)
// Promise {<rejected>: AggregateError: All promises were rejected}
复制代码
只要可迭代对象里包含任何一个 fulfilled
状态的 Promise
,则会返回第一个 fulfilled
的实例,而且以它的值做为新实例的值:
let p = Promise.any([
1,
Promise.reject(2),
new Promise((resolve, reject) => {}),
Promise.resolve(3),
])
setTimeout(console.log, 0, p)
// Promise {<fulfilled>: 1}
复制代码
其余状况下,都会返回一个 pending
状态的实例:
let p = Promise.any([
Promise.reject(2),
Promise.reject(3),
new Promise((resolve, reject) => {}),
])
setTimeout(console.log, 0, p)
// Promise {<pending>: undefined}
复制代码
咱们知道一个普通的引用(默认是强引用)会将与之对应的对象保存在内存中。只有当该对象没有任何的强引用时,JavaScript
引擎 GC
才会销毁该对象而且回收该对象所占的内存空间。
WeakRef
对象容许你保留对另外一个对象的弱引用,而不会阻止被弱引用的对象被 GC
回收。WeakRef
的实例方法 deref()
能够返回当前实例的 WeakRef
对象所绑定的 target
对象,若是该 target
对象已被 GC
回收则返回 undefined
:
let person = { name: '布兰', age: 12 }
let wr = new WeakRef(person)
console.log(wr.deref())
// { name: '布兰', age: 12 }
复制代码
正确使用 WeakRef 对象须要仔细的考虑,最好尽可能避免使用。这里面有诸多缘由,好比:GC 在一个 JavaScript 引擎中的行为有可能在另外一个 JavaScript 引擎中的行为截然不同,或者甚至在同一类引擎,不一样版本中 GC 的行为都有可能有较大的差距。GC 目前仍是 JavaScript 引擎实现者不断改进和改进解决方案的一个难题。
参考:
逻辑赋值符包含 3 个:
x &&= y
:逻辑与赋值符,至关于 x && (x = y)
x ||= y
:逻辑或赋值符,至关于 x || (x = y)
x ??= y
:逻辑空赋值符,至关于 x ?? (x = y)
看以下示例,加深理解:
let x = 0
x &&= 1 // x: 0
x ||= 1 // x: 1
x ??= 2 // x: 1
let y = 1
y &&= 0 // y: 0
y ||= null // y: null
y ??= 2 // y: 2
复制代码
对于下面一串数字,你一眼看上去不肯定它究竟是多少吧?
const num = 1000000000
复制代码
那如今呢?是否是能够很清楚的看出来它是 10 亿:
const num = 1_000_000_000
复制代码
数值分隔符(_
)的做用就是为了让数值的可读性更强。除了能用于十进制,还能够用于二级制,十六进制甚至是 BigInt
类型:
let binarary = 0b1010_0001_1000_0101
let hex = 0xA0_B0_C0
let budget = 1_000_000_000_000n
复制代码
使用时必须注意 _
的两边必需要有类型的数值,不然会报错,如下这些都是无效的写法:
let num = 10_
let binarary = 0b1011_
let hex = 0x_0A0B
let budget = 1_n
复制代码