ES2020来啦!快看!

402 阅读8分钟

你到现在还只知道ES2015(ES6)?ES2020都出来啦!马上撸起来~

动态import()

实际上,动态import()很早就提出来了,并且有实验的版本,但是需要通过babel来编译成浏览器支持的语法。现在,浏览器已经原生支持动态import()啦!

静态import的用法

静态import在代码执行前就”绑定“了当前的作用域,并且只能用在文件的最顶层。

举个栗子,有如下模块./utils.mjs

export default () => {
  console.log('Hi from the default export!')
}
export const doStuff = () => {
  console.log('Doing stuff…')
}

以下代码演示如何静态引入utils.mjs模块:

<script type='module'>
  import * as module from './utils.mjs'
  module.default()
  module.doStuff()
</script>

静态import使得诸如静态分析、构建工具、tree-shaking等应用场景可行。但是,并不支持以下场景:

  • 按需引入模块;
  • 在运行时计算模块的标识符。

动态import()的用法

动态import()引入了一种以函数形式来适应静态import所不支持的应用场景。import(moduleSpecifier)返回一个promise对象。

以下代码演示如何动态引入utils.mjs模块:

<script type="module">
  const moduleSpecifier = './utils.mjs'
  import(moduleSpecifier)
    .then((module) => {
      module.default()
      module.doStuff()
    })
</script>

由于import()返回一个promise对象,所以可以使用async/await来代替then形式的回调方式:

<script type="module">
  (async () => {
    const moduleSpecifier = './utils.mjs'
    const module = await import(moduleSpecifier)
    module.default()
    module.doStuff()
  })()
</script>

BigInt:JS中任意精度的整数类型

在JS中,Number表示双精度浮点数,这意味着Number具有有限的精度。从Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGER。正如JS开发者皆知的0.10 + 0.20 !== 0.30

BigInt是JS中一种新的原始数字类型,它可以表示任意精度的整数。该功能在Chrome 67开始引入。

如何创建一个BigInt

只要添加一个n后缀到任意整型,就可以创建一个BigInt。比如,123变成123n,全局的BigInt(number)函数就会将Number转换成一个BigInt。

const max = Number.MAX_SAFE_INTEGER
console.log(max)                // 9007199254740991
console.log(max + 1)            // 9007199254740992
console.log(max + 2)            // 9007199254740992,wrong answer!
console.log(BigInt(max) + 2n)   // 9007199254740993n,correct!

1234567890123456789 * 123       // 151851850485185200000,wrong answer!
1234567890123456789n * 123n     // 151851850485185185047n,correct!

BigInt:一种新的原始类型

typeof 123    // 'number'
typeof 123n   // 'bigint'

42n === BigInt(42)  // true
42n == 42           // true
42n === 42          // false

if (0n) {
  console.log('if')
else {
  console.log('else'// 'else'
}

BigInt支持的运算符

BigInt支持绝大部分场景的运算符,包括二元运算符+、-、*、**;/、%,位运算符|、&、<<、>>、^。

(7 + 6 - 5) * 4 ** 3 / 2 % 3          // 1
(7n + 6n - 5n) * 4n ** 3n / 2n % 3n   // 1n

-42n  // 表示负数
+42n  // 报错,一元运算符+只能用于Number类型

**注意:JS不允许混用BigInt和Number,因为这样会丢失精度。**比如:

BigInt(Number.MAX_SAFE_INTEGER) + 2.5   // 报错

但是,有个例外,BigInt和Number可以用于比较,因为比较并不会丢失精度:

123 < 124n    // true

BigInt API

  • BigInt.asIntN(width, value):将一个BigInt转换成一个width位的有符号整数。
  • BigInt.asUintN(width, value):将一个BigInt转换成一个width位的无符号整数.
  • BigInt64Array(length)
  • BigUint64Array(length)
BigInt(123)     // 123n
BigInt(1.5)     // RangeError
BigInt('1.5')   // SyntaxError

// Highest possible BigInt value that can be represented as a signed 64-bit integer.
const max = 2n ** (64n - 1n) - 1n
BigInt.asIntN(64, max)      // 9223372036854775807n
BigInt.asIntN(64, max + 1n) // -9223372036854775808n,negative because of overflow

const view = new BigInt64Array(4// [0n, 0n, 0n, 0n]
view.length     // 4
view[0]         // 0n
view[0] = 42n
view[0]         // 42n

Module namespace exports

在JS模块中,以下语法已经被支持:

import * as utils from './utils.mjs'

然而,没有对应的export语法。但现在有了:

export * as utils from './utils.mjs'
// 该语句相当于:
import * as utils from './utils.mjs'
export { utils }

String.prototype.matchAll

  • matchAll尤其适用于具有捕获组(capture groups)的正则表达式。
  • 注意:调用matchAll必须使用一个全局搜索的正则,否则会报错。
const string = 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev'
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g
for (const match of string.matchAll(regex)) {
  console.log(`${match[0]} at ${match.index} with '${match.input}'`)
  console.log(`→ owner: ${match.groups.owner}`)
  console.log(`→ repo: ${match.groups.repo}`)
}
// 输出:
// tc39/ecma262 at 23 with 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev'
// → owner: tc39
// → repo: ecma262
// v8/v8.dev at 36 with 'Favorite GitHub repos: tc39/ecma262 v8/v8.dev'
// → owner: v8
// → repo: v8.dev

Promise Combinators

  • Promise.all:只要有任意一个promise状态变为rejected,就会”短路“。ES2015引入。
  • Promise.race:只要有任意一个promise完成(fulfilled/rejected),就会”短路“。ES2015引入。
  • Promise.allSettled:等待所以promise完成(fulfilled/rejected)才返回,不会”短路“。ES2020引入。
  • Promise.any:只要有任意一个promise状态变为fulfilled,就会”短路“。还处于proposal中。

Promise.all

const promises = [
  fetch('/component-a.css'),
  fetch('/component-b.css'),
  fetch('/component-c.css'),
]
try {
  const styleResponses = await Promise.all(promises)
  enableStyles(styleResponses)
  renderNewUi()
catch (reason) {
  displayError(reason)
}

Promise.race

try {
  const result = await Promise.race([
    performHeavyComputation(),
    rejectAfterTimeout(2000),
  ])
  renderResult(result)
catch (error) {
  renderError(error)
}

Promise.allSettled

// Imagine some of these requests fail, and some succeed.
const promises = [
  fetch('/api-call-1'),
  fetch('/api-call-2'),
  fetch('/api-call-3'),
]
await Promise.allSettled(promises)
// All API calls have finished (either failed or succeeded).
removeLoadingIndicator()

Promise.any

const promises = [
  fetch('/endpoint-a').then(() => 'a'),
  fetch('/endpoint-b').then(() => 'b'),
  fetch('/endpoint-c').then(() => 'c'),
]
try {
  const first = await Promise.any(promises)
  // Any of the promises was fulfilled.
  console.log(first)
catch (error) {
  // All of the promises were rejected.
  console.log(error)
}

globalThis

在之前,我们需要做很多判断来获取全局this,比如,浏览器中的window,node环境中的global。现在,globalThis关键字引入了一种统一的机制来访问任意JS环境中的全局this值。Chrome 71+支持。

const theGlobalThis = globalThis

Optional chaining

Optional chaining在Chrome 80+开始支持。

在optional chaining出现之前

对于对象深层次的属性访问,很容易出现引用错误:

// 很可能会出错
const nameLength = db.user.name.length;

// 不会出错,但不易读
let nameLength
if (db && db.user && db.user.name) {
  nameLength = db.user.name.length
}

optional chaining的用法

// 仍然会检查错误,并且更加易读
const nameLength = db?.user?.name?.length

// 调用optional方法:
const adminOption = db?.user?.validateAdminAndGetPrefs?.().option

// 访问动态的属性名:
const optionName = 'optional setting'
const optionLength = db?.user?.preferences?.[optionName].length

// optional数组下标,若数组为null/undefined,返回undefined:
const userIndex = 42
const userName = usersArray?.[userIndex].name

optional chaining的特性

  • Short-circuiting:若optional chaining提前返回了,后面的语句则不会再执行。
  • Stacking:属性访问中可以有多个optional chaining操作符。
  • Optional deletion:delete操作符可以和optional chaining组合使用。
// age is incremented only if db and user are defined.
db?.user?.grow(++age)

// An optional chain may be followed by another optional chain.
const firstNameLength = db.users?.[42]?.names.first.length

// db.user is deleted only if db is defined.
delete db?.user

Nullish coalescing

Chrome 80+开始支持Nullish coalescing——??

false ?? true     // => false
0 ?? 1            // => 0
'' ?? 'default'   // => ''

null ?? []        // => []
undefined ?? []   // => []

const link = document.querySelector('link') ?? document.createElement('link')
const link = obj.deep?.container.link ?? document.createElement('link')

本文章来自我的微信公众号:前端酱的日常

欢迎关注我的微信公众号!持续更新中! 前端酱的日常微信公众号二维码

本文使用 mdnice 排版