本文简略记录了一下ES2018到ES2021中新增的特性,可以点击新特性详细了解一下。
ES2018/ES9
非转义序列的模板字符串
移除了在带标签的模板字符串中转义序列的语法限制
之前,\u
开始一个unicode转义,\x
开始一个十六进制转义,\
后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:\uuu\xxx\111
正则表达式Unicode转义
之前, 可以通过字符集的名称来匹配字符, 即s
代表空白/^\s+$/u.test(' ') // true
在es9,添加了Unicode属性转义,形式为\p{...}
和\P{...}
,在正则表达式使用标记u
/^\p{White_Space}+$/u.test(' ') // 空格
// true
/^\p{Script=Greek}+$/u.test('μετά') // 希腊字母
// true
/^\p{Script=Latin}+$/u.test('Grüße') // 匹配拉丁字母
正则表达式s标记
之前.
可以匹配任意字符,除了换行符
es9后,可以通过标记s
,这样.
就可以匹配换行符/hello.es9/s.test('hello\nes9') //true
RegExp named capture groups(正则表达式命名捕获组)
捕获分组和非捕获分组
()
表示捕获分组,()
会把每个分组里的匹配的值保存起来(存储在内存中),捕获的子序列稍后可以通过 Back 引用(反向引用) 在表达式中使用
以(?)
开头的就是非捕获分组,不会将匹配的值保存起来,也不能后面引用,其中?:、?=、?<=
等都是非捕获元,使用这些非捕获元的分组为非捕获分组
捕获分组都是通过索引编号的, 这样代码可读性差
const reg = /(\d{4})-(\d{2})-(\d{2})/u;
const matched = reg.exec('2018-12-31');
matched[0]; // 2018-12-12
matched[1]; // 2018
matched[2]; // 12
matched[3]; // 31
命名捕获组
es9中,允许命名捕获组使用符号?<name>
, 小括号中匹配内容的名称放在groups里,提高了代码的可读性
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u
const matched = reg.exec('2018-12-31')
matched.groups.year; // 2018
matched.groups.month; // 12
matched.groups.day; // 31
命名捕获组也可以使用在replace()方法中
例如将日期转换为“年月日”格式:
const reg = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
'2018-12-31'.replace(reg, '$<year>年$<month>月$<day>日');
// 2018年12月31日
正则表达式反向断言(lookbehind)
正向先行断言:?=
形式: (?=pattern)
exp1(?=exp2): 查找后面为exp2的exp1
const reg = /runoob(?=[\d+])/g //匹配数字前面的runoob
123456runoob123runoobbdhh // 只会匹配第一个runoob
23runoob456runoob789 //两个都好匹配到
负向先行断言: ?!
形式: (?!pattern)
exp1(?!exp2): 查找后面不为exp2的exp1
const reg = /runboo(?![0-9]+)/g // 匹配后面不是数字的runboo
1233runboo-google12runoob1233 // 匹配第一个
正向后行断言(es9): ?<=
形式: (?<=pattern)
(?<=exp2)exp1: 查找前面是exp2的exp1
const reg = /(?<=[0-9]+)runboo/g //匹配前面是数字的runboo
1234google123runboo456 // 匹配runboo
123googlerunboo123runboo456 // 匹配第二个
负向后行断言(es9):?<!
形式: (?<!pattern)
(?<!exp2)exp1: 查找前面不是exp2的exp1
const reg = /(?<=[0-9]+)runboo/g //匹配前面不是数字的runboo
1234google123runboo456 // 不能匹配到runboo
123googlerunboo123runboo456 // 匹配第一个
扩展运算符(...)
之前...
只能用于数组,es9后,也可以用于对象了,用法和数组一样
Promise.finally()
没有参数
之前的Promise的结果要么是成功then
要么是失败catch
,使得有些公共逻辑代码必须在两个回调函数里面写(例如:执行状态修改, 删除对话等)
然后就有了finally()
,逻辑只放在一个地方,无论Promise运行成功还是失败,都会运行
new Promise((reslove, reject) => {
// ...
}).then((res) => {
// reslove
}).catch((err) => {
// reject
}).finally(() => {
// complete
});
异步迭代
es8中,可以使用async/await
在同步的写法中执行异步函数,但是在循环中,循环依然保持同步,即在内部异步函数调用之前循环已经全部完成
async function foo(array) {
for (let i of array) {
await doSomething(i);
}
}
es9引入了异步迭代器(asynchronous iterators),使await
可以和for...of
循环一起使用
async function foo (array) {
for await (let i of array) {
doSomething(i)
}
}
ES2019/ES10
可选的catch
try {
//...
} cath(unused) {
// ...
}
//可以省略没有使用到的catch里的变量
try {
//...
} catch {
//...
}
Symbol.prototype.description
只读属性,返回Symbol对象的可描述的字符串
Symbol('desc').description // desc
Object.fromEntries(iterable)
将键值对列表转换为对象
Object.fromEntries() 执行与 Object.entries 互逆的操作:
//将对象转换为数组
const arr = Object.entries(obj) // [[key, value]]的形式
// 还原回来
const obj = Object.fromEntries(arr);
将map对象转为对象:
const map = new Map();
map.set('one', 1);
map.set('two', 2);
const obj = Object.fromEntries(map); // {one: 1, two: 2}
处理URL的查询字符串:
const paramsString = 'param1=foo¶m2=baz';
const searchParams = new URLSearchParams(paramsString); // { param1: 'foo', param2: 'baz'}
String.prototype.{trimStart, trimEnd}
分别为去掉字符串左边和右边的空格,与trimLeft()和trimRight()相同
对于Web兼容性,trimLeft() 和trimRight() 将保留为trimStart() 和trimEnd() 的别名。
Array.prototype.{flat, faltMap}
flat([level])
方法可以将多维数组展平成一维数组,且有空值时,会丢弃; 可选参数level指定展开几层
// 以前使用concat或reduce
const arr = ['a', 'b', ['c', 'd']];
const flattend = [].concat.apply([], arr);
// const flattend = [].concat(...arr);
flatMap()方法将map()和flat()组合成一个方法
const arr2 = [[7.1], [8.1], [9.1], [10.1], [11.1]];
//想要删除项目,只需返回[]
// do not include items bigger than 9
arr.flatMap(value => {
if (value >= 10) {
return [];
} else {
return Math.round(value);
}
});
// [7, 8,9]
ES2020/ES11
String.prototype.matchAll(regexp)
返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器
regexp参数必须要设置全局模式g
,否则会抛出异常
之前我们通过在循环中调用regexp.exec()
来获取所有匹配项信息
const regexp = RegExp('foo[a-z]*','g');
const str = 'table football, foosball';
let match;
while ((match = regexp.exec(str)) !== null) {
console.log(`Found ${match[0]} start=${match.index} end=${regexp.lastIndex}.`);
// expected output: "Found football start=6 end=14."
// expected output: "Found foosball start=16 end=24."
}
// matchAll
const matches = str.matchAll(regexp);
for (const match of matches) {
console.log(`Found ${match[0]} start=${match.index} end=${match.index + match[0].length}.`);
}
// expected output: "Found football start=6 end=14."
// expected output: "Found foosball start=16 end=24."
import()
按需动态导入模块
在之前,根据需要导入模块时只能使用require()@babel/preset-env
中已经包含了@babel/plugin-syntax-dynamic-import
,所有只需配置@babel/preset-env
import()返回的是一个Promise对象
// module.js
export default {
name: 'shenjp'
}
// index.js
if (true) {
let module = import('./module.js');
console.log(module); // Promise {<pending>
module.then(data => console.log(data)); // Module {default: {name: "shenjp"}, __esModule: true, Symbol(Symbol.toStringTag): "Module"}
}
import.meta
为当前运行的模块添加了一个特定host元数据对象
<script type="module" src="my-module.mjs"></script>
console.log(import.meta); // { url: "file:///home/user/my-module.mjs" }
BigInt:内置对象
可以表示任意大的整数,原本JavaScript只能表示2^53 -1以内的数字
定义一个BigInt:
// 1、在数字字面量后面加一个n
const big = 10n
// 2、调用BigInt()
const big2 = BigInt(9007199254740991) // 9007199254740991n
const big3 = BigInt('9007199254740991') // 9007199254740991n
typeof big2 // bigint
// 带小数的运算会被取整
5n/2n // 2n
注意: 不能用于Math对象中的方法;不能和Number实例混合运算,必须要转换为同一种类型才行(在BigInt转Number时,注意可能会丢失精度)
BigInt可以和+、*、-、**(次方)、%
和除>>>
(因为BigInt都是有符号的)之外的位操作
空值合并运算符(??)
只会左边的值严格等于null或undefined时才使用右边的值
'' || 'default' // default
'' ?? 'default' // ''
0 || 'defa' // defa
0 ?? 'defa' // defa
可选链
当我们在一个深的树形结构中查找一个属性值会像下面这样:
const name = data && data.info && data.info.name
//使用?.
const name1 = data?.info?.name
//还可以与空值合并运算符搭配使用
const name2 = data?.info?.name ?? 'jack'
// 其他使用
a?.b // a === null ? undefined : a.b
a?.[x] // a === null ? undefined : a[x]
a?.b() // a === null ? undefined : a.b()
a?.() // a === null ? undefined : a()
a?.b[3].c?.(x).d // a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
// optional deletion
delete a?.b // a === null ? true : delete a.b
不支持可选链的:
1、可选的构造函数: new a?.()
2、可选的模块字符串: a?.`string`
3、可选的属性赋值: a?.b = c
4、可选的super: super?.(), super?.foo
globalThis
以前,在不同的JavaScript环境中获取全局对象需要不同的的语句,Web中可以通过window、self(Web workers)、frames取到全局对象,在Node.js中,使用global获得
现在可以统一使用globalThis来获取全局对象
Promise.allSettled()
返回一个在给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果
const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'foo'));
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result.status)));
// fulfilled
// rejected
ES2021/ES12
String.prototype.replaceAll(searchValue, replaceValue)
已有的String.prototype.replace
只能替换第一个匹配的,如果需要匹配所有,需要使用正则如果加全局符合g
还可以使用split+join
的方法达到替换所有
注意: replaceAll
时,searchValue
如果是正则但是是非全局时(即没有加g
关键字),会引发异常,
Promise.any()
接收一个Promise可迭代对象,只要其中的一个promise成功,就返回那个已经成功的promise,如果没有一个promise成功,则返回一个失败的Promise
如果传入的参数是一个空的迭代对象,则会返回一个已失败状态的Promise
如果传入的参数不包含任何promise,则返回一个异步完成的promise
该方法会返回第一个成功的 promise 。只要有一个 promise 成功此方法就会终止,它不会等待其他的 promise 全部完成。
Promise.any([
fetch('https://v8.dev/').then(() => 'home'),
fetch('https://v8.dev/blog').then(() => 'blog'),
fetch('https://v8.dev/docs').then(() => 'docs')
]).then((first) => {
// Any of the promises was fulfilled.
console.log(first);
// → 'home'
}).catch((error) => {
// All of the promises were rejected.
console.log(error);
});
Logical Assignment Operators(逻辑赋值操作符)
a ||= b // a || (a = b)
a ??= b
a &&= b
数字分隔符(_)
在二进制、十六进制、BigInt中都可以使用
const x = 1000000000
const y = 1000_000_000
x === y // true
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。