新特性:
String.prototype.matchAll
import()
import.meta
-
BigInt
– 任意精度整数 Promise.allSettled
globalThis
for-in
- 可选链 —
?.
- 空值合并运算符 —
??
export * as ns from "mod"
String.prototype.matchAll()
matchAll()
返回全局正则表达式的所有匹配项。
给定一个字符串或一个正则表达式,matchAll()
在所有匹配项的匹配对象上返回一个可迭代对象。
let regexp = /t(e)(st(\d?))/
let str = 'test1test2'
// 使用扩展运算符 `(...)` 将可迭代对象转换为数组:
let array = [...str.matchAll(regexp)]
array[0] // ["test1", "e", "st1", "1"]
array[1] // ["test2", "e", "st2", "2"]
注意:必须设置
g
修饰符
RegExp.prototype.exec()
与 /g
在以下示例中,我们收集数组匹配中组 1 的所有捕获:
function collectGroup1(regExp, str) {
const matches = []
while (true) {
const match = regExp.exec(str)
if (match === null) break
// 将组 1 的捕获添加到 matches
matches.push(match[1])
}
return matches
}
collectGroup1(/"([^"]*)"/ug, `"foo" and "bar" and "baz"`) // [ 'foo', 'bar', 'baz' ]
没有标志 /g
,exec()
始终只返回第一个匹配项:
const re = /[abc]/
re.exec('abc') // [ 'a', index: 0, input: 'abc' ]
re.exec('abc') // [ 'a', index: 0, input: 'abc' ]
这对 collectGroup1()
来说是个坏消息,因为如果 regExp
没有标志 /g
,它将永远不会完成。
String.prototype.match()
与 /g
如果你使用 match()
,使用设置了标志 /g
的正则表达式,可以在一个数组中获得它的所有完整匹配项(换句话说,捕获组被忽略):
'abab'.match(/a/ug) // [ 'a', 'a' ]
如果未设置 /g
,match()
的工作原理与 RegExp.prototype.exec()
类似:
'abab'.match(/a/u) // [ 'a', index: 0, input: 'abab' ]
String.prototype.replace()
与 /g
你可以使用一个技巧通过收集捕获 replace()
:我们使用一个函数来计算替换值。该函数接收所有捕获信息。但是,它不计算替换值,而是在数组匹配中收集感兴趣的数据:
function collectGroup1(regExp, str) {
const matches = []
function replacementFunc(all, first) {
matches.push(first)
}
str.replace(regExp, replacementFunc)
return matches
}
collectGroup1(/"([^"]*)"/ug, `"foo" and "bar" and "baz"`) // [ 'foo', 'bar', 'baz' ]
对于不带标志 /g
的正则表达式,replace()
只访问第一个匹配项。
RegExp.prototype.test()
test()
只要正则表达式匹配,就返回 true
:
const regExp = /a/ug
const str = 'aa'
regExp.test(str) // true
regExp.test(str) // true
regExp.test(str) // false
String.prototype.split()
split()
可以拆分字符串并使用字符串或正则表达式指定分隔符。如果该正则表达式至少包含一个捕获组,则 split()
返回一个数组,其中子字符串与第一个组捕获的内容交错:
const regExp = /<(-+)>/ug
const str = 'a<--->b<->c'
str.split(regExp) // [ 'a', '---', 'b', '-', 'c' ]
动态导入 import()
在此之前,我们只能使用静态导入,它只接受模块路径的字符串。使用动态导入,我们必须使用 Promise 有条件地导入模块。
<nav>
<a href="books.html" data-entry-module="books">Books</a>
<a href="movies.html" data-entry-module="movies">Movies</a>
<a href="video-games.html" data-entry-module="video-games">Video Games</a>
</nav>
<main>Content will load here!</main>
const main = document.querySelector('main')
for (const link of document.querySelectorAll('nav > a')) {
link.addEventListener('click', (e) => {
e.preventDefault()
import(`./section-modules/${link.dataset.entryModule}.js`)
.then((module) => {
module.loadPageInto(main)
})
.catch((err) => {
main.textContent = err.message
})
})
}
点击此处👉 Loading modules dynamically via import() 了解更多内容。
import.meta
用于保存有关当前模块的特定于主机的元数据的元属性。
(async () => {
const response = await fetch(new URL("../hamsters.jpg", import.meta.url))
const blob = await response.blob()
const size = import.meta.scriptElement.dataset.size || 300
const image = new Image()
image.src = URL.createObjectURL(blob)
image.width = image.height = size
document.body.appendChild(image)
})()
点击此处👉 import.meta – metadata for the current module 了解更多内容。
BigInt
— 任意精度整数
BigInt
一种用于大整数运算的新原始数据类型,表示大于 2⁵³ 的数字。
BigInt
使用一系列数字,后缀为 n
。
const theBiggestInt = 9886881870000166n
const alsoHuge = BigInt(9886881870000166) // 9007199254740991n
const hugeButString = BigInt('9886881870000166') // 9886881870000166n
typeof
的返回值为 bigint
:
typeof 123n // 'bigint'
Promise.allSettled()
当所有给定的 Promise 都得到解决时返回(resolved
或 rejected
,无关紧要)。
const sleep = sm => new Promise(resolve => setTimeout(() => resolve(sm), sm))
const err = (ms) => sleep(ms).then(() => Promise.reject(ms))
Promise.allSettled([1, 2, 3]).then(console.log)
Promise.allSettled([sleep(300), sleep(100), sleep(200)]).then(console.log)
Promise.allSettled([sleep(3000), err(100), sleep(2000)]).then(console.log)
Promise.allSettled([err(50), err(60)]).then(console.log)
globalThis
在此之前,全局对象由于不同的 JavaScript 环境有不同的语法:
- 在 Web 浏览器可以是
window
,self
,frames
或this
。 - 对于 Web Worker 来说,它是
self
。 - 在 Node.js 中,它是
global
。
globalThis
为所有 JavaScript 环境中的全局对象提供了单一语法。
// node 环境
console.log(globalThis)
/**
<ref *1> Object [global] {
global: [Circular *1],
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setInterval: [Function: setInterval],
setTimeout: [Function: setTimeout] {
[Symbol(nodejs.util.promisify.custom)]: [Function (anonymous)]
},
queueMicrotask: [Function: queueMicrotask],
clearImmediate: [Function: clearImmediate],
setImmediate: [Function: setImmediate] {
[Symbol(nodejs.util.promisify.custom)]: [Function (anonymous)]
}
}
*/
// 浏览器
globalThis // Window {0: global, 1: global, window: Window, self: Window, …}
注意:
globalThis
并不总是直接指向全局对象。详细可查阅 In browsers, globalThis does not point directly to the global object。
但 globalThis
会对性能产生负面影响,也可能会产生一些不到的错误,建议使用 ES6 的一些特性避免全局对象,例如:
- 声明在
const
、let
和class
全局作用域内,使用时不会创建全局对象属性。 - 每个 ES 模块都有自己的局部作用域。(默认开启严格模式)
可选链 — ?.
可选链 ?.
是一种访问嵌套对象属性的安全的方式。即使中间的属性不存在,也不会出现错误。
如果问号前面的值不是 undefined
也不是 null
,则执行问号后面的操作。否则,返回 undefined
。它有如下三种语法:
obj?.prop // 可选的静态属性访问
obj?.[prop] // 可选的动态属性访问
func?.(...args) // 可选方法调用
-
obj?.prop
— 如果obj
存在则返回obj.prop
,否则返回undefined
。 -
obj?.[prop]
— 如果obj
存在则返回obj[prop]
,否则返回undefined
。 -
obj.method?.(...args)
— 如果obj.method
存在则调用obj.method()
,否则返回undefined
。
const user = {}
user.address.street // TypeError
user?.address?.street // undefined
可选链的替代方案
&&
user && user.address && user.address.street // undefined
缺点:
- 语法冗长
- 如果失败,
&&
返回其左侧,而?.
始终返回undefined
: -
&&
对所有虚值的左侧都失败,而?.
只对undefined
和null
失败:
解构
您还可以使用解构来处理链式属性访问,但这并不美观:
const { address: { street = undefined } = {} } = user
street // undefined
第三方库:Lodash 库的 get()
方法
_.get(user, 'address.street')
支持情况
可选链(?.
)已经支持大部分的主流浏览器:
![](https://img.haomeiwen.com/i18281896/6268201ef1e6d507.png)
如果您需要支持 IE 或更低版本浏览器,babel 有一个插件 — @babel/plugin-proposal-optional-chaining
。
点击此处👉 可选链 "?." 了解更多内容。
空值合并运算符 — ??
??
为二元运算符。如果左侧表达式的值为 null
或 undefined
,则计算运算符的右侧。
const data = {
nullValue: null,
height: 400,
animationDuration: 0,
headerText: '',
showSplashScreen: false
}
console.log(data.undefinedValue ?? 'some other default') // 'some other default'
console.log(data.nullValue ?? 'some other default') // 'some other default'
console.log(data.headerText ?? 'Hello, world!') // ''
console.log(data.animationDuration ?? 300) // 0
console.log(data.showSplashScreen ?? true) // false
可以看到,它只关心左侧的值是否为 null
或 undefined
,而不在乎是否为虚值。
点击此处👉 空值合并运算符 '??' 了解更多内容。
export * as ns from 'mod'
语法:
export * as ns from 'module'
示例:
export * as ns from './utils'
// 👆 等同于 👇
import * as ns from './utils'
export { ns }
扩展
Airbnb 规范建议我们不要使用通配符导入。
原因:确保您有一个默认导出。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide'
// good
import AirbnbStyleGuide from './AirbnbStyleGuide'
并且不要直接从 import
中 export
原因:尽管单行代码很简洁,但是使用一种清晰的导入方式和一种清晰的导出方式可以使事情保持一致。
// bad
// es6.js
export { es6 as default } from './AirbnbStyleGuide'
// good
// es6.js
import { es6 } from './AirbnbStyleGuide'
export default es6
点击此处👉Normative: Add export * as ns from "mod” to Export production and Module Semantic 了解更多详情。
指定 for-in
枚举顺序
不同的引擎已就如何指定 for-in
枚举顺序达成一致,从而使行为标准化。
let obj = {
p1: 'p1',
p2: 'p2',
p3: 'p3'
}
let keys = []
for (let key in o) {
if (key === 'p1') {
o.p4 = 'p4'
}
keys.push(key)
}
网友评论