ES6、ES7、ES8、ES9、ES10新特性一览
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6、ES7、ES8、ES9、ES10新特性一览相关的知识,希望对你有一定的参考价值。
参考技术A
ECMA规范最终由TC39敲定。TC39由包括浏览器厂商在内的各方组成,他们开会推动javascript提案沿着一条严格的发展道路前进。 从提案到入选ECMA规范主要有以下几个阶段:
ES6的特性比较多,在 ES5 发布近 6 年(2009-11 至 2015-6)之后才将其标准化。两个发布版本之间时间跨度很大,所以ES6中的特性比较多。 在这里列举几个常用的:
1.类(class)
对熟悉Java,object-c,c#等纯面向对象语言的开发者来说,都会对class有一种特殊的情怀。ES6 引入了class(类),让JavaScript的面向对象编程变得更加简单和易于理解。
2.模块化(Module)
ES5不支持原生的模块化,在ES6中模块作为重要的组成部分被添加进来。模块的功能主要由 export 和 import 组成。每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过import来引用其它模块提供的接口。同时还为模块创造了命名空间,防止函数的命名冲突。
导出(export)
ES6允许在一个模块中使用export来导出多个变量或函数。
导出变量
ES6将一个文件视为一个模块,上面的模块通过 export 向外输出了一个变量。一个模块也可以同时往外面输出多个变量。
导出函数
导入(import)
定义好模块的输出以后就可以在另外一个模块通过import引用。
3.箭头(Arrow)函数
这是ES6中最令人激动的特性之一。=>不只是关键字function的简写,它还带来了其它好处。箭头函数与包围它的代码共享同一个this,能帮你很好的解决this的指向问题。有经验的JavaScript开发者都熟悉诸如var self = this;或var that = this这种引用外围this的模式。但借助=>,就不需要这种模式了。
箭头函数的结构
箭头函数的箭头=>之前是一个空括号、单个的参数名、或用括号括起的多个参数名,而箭头之后可以是一个表达式(作为函数的返回值),或者是用花括号括起的函数体(需要自行通过return来返回值,否则返回的是undefined)。
卸载监听器时的陷阱
除上述的做法外,我们还可以这样做:
4.函数参数默认值
ES6支持在定义函数的时候为其设置默认值:
这样写一般没问题,但当参数的布尔值为false时,就会有问题了。比如,我们这样调用foo函数:
foo(0, "")
因为0的布尔值为false,这样height的取值将是50。同理color的取值为‘red’。
所以说,函数参数默认值不仅能是代码变得更加简洁而且能规避一些问题。
5.模板字符串
ES6支持模板字符串,使得字符串的拼接更加的简洁、直观。
在ES6中通过$就可以完成字符串的拼接,只需要将变量放在大括号之中。
6.解构赋值
解构赋值语法是JavaScript的一种表达式,可以方便的从数组或者对象中快速提取值赋给定义的变量。
获取数组中的值
从数组中获取值并赋值到变量中,变量的顺序与数组中对象顺序对应。
如果没有从数组中的获取到值,你可以为变量设置一个默认值。
通过解构赋值可以方便的交换两个变量的值。
获取对象中的值
7.延展操作符(Spread operator)
延展操作符...可以在函数调用/数组构造时, 将数组表达式或者string在语法层面展开;还可以在构造对象时, 将对象表达式按key-value的方式展开。
语法
应用场景
没有展开语法的时候,只能组合使用 push,splice,concat 等方法,来将已有数组元素变成新数组的一部分。有了展开语法, 构造新数组会变得更简单、更优雅:
和参数列表的展开类似, ... 在构造字数组时, 可以在任意位置多次使用。
展开语法和 Object.assign() 行为一致, 执行的都是浅拷贝(只遍历一层)。
在ECMAScript 2018中延展操作符增加了对对象的支持
8.对象属性简写
在ES6中允许我们在设置一个对象的属性的时候不指定属性名。
对象中必须包含属性和值,显得非常冗余。
对象中直接写变量,非常简洁。
9.Promise
Promise 是异步编程的一种解决方案,比传统的解决方案callback更加的优雅。它最早由社区提出和实现的,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
嵌套两个setTimeout回调函数:
上面的的代码使用两个then来进行异步编程串行化,避免了回调地狱:
10.支持let与const
在之前JS是没有块级作用域的,const与let填补了这方便的空白,const与let都是块级作用域。
ES2016添加了两个小的特性来说明标准化过程:
1.Array.prototype.includes()
includes() 函数用来判断一个数组是否包含一个指定的值,如果包含则返回 true,否则返回false。
includes 函数与 indexOf 函数很相似,下面两个表达式是等价的:
接下来我们来判断数字中是否包含某个元素:
使用indexOf()验证数组中是否存在某个元素,这时需要根据返回值是否为-1来判断:
使用includes()验证数组中是否存在某个元素,这样更加直观简单:
2.指数操作符
在ES7中引入了指数运算符**,**具有与Math.pow(..)等效的计算结果。
使用自定义的递归函数calculateExponent或者Math.pow()进行指数运算:
使用指数运算符**,就像+、-等操作符一样:
1.async/await
ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。例如:
2.Object.values()
Object.values()是一个与Object.keys()类似的新函数,但返回的是Object自身属性的所有值,不包括继承的值。
假设我们要遍历如下对象obj的所有值:
从上述代码中可以看出Object.values()为我们省去了遍历key,并根据这些key获取value的步骤。
3.Object.entries()
Object.entries()函数返回一个给定对象自身可枚举属性的键值对的数组。
接下来我们来遍历上文中的obj对象的所有属性的key和value:
4.String padding
在ES8中String新增了两个实例函数String.prototype.padStart和String.prototype.padEnd,允许将空字符串或其他字符串添加到原始字符串的开头或结尾。
5.函数参数列表结尾允许逗号
主要作用是方便使用git进行多人协作开发时修改同一个函数减少不必要的行变更。
6.Object.getOwnPropertyDescriptors()
Object.getOwnPropertyDescriptors()函数用来获取一个对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
返回obj对象的所有自身属性的描述符,如果没有任何自身属性,则返回空对象。
7.SharedArrayBuffer对象
SharedArrayBuffer 对象用来表示一个通用的,固定长度的原始二进制数据缓冲区,类似于 ArrayBuffer 对象,它们都可以用来在共享内存(shared memory)上创建视图。与 ArrayBuffer 不同的是,SharedArrayBuffer 不能被分离。
8.Atomics对象
Atomics 对象提供了一组静态方法用来对 SharedArrayBuffer 对象进行原子操作。
这些原子操作属于 Atomics 模块。与一般的全局对象不同,Atomics 不是构造函数,因此不能使用 new 操作符调用,也不能将其当作函数直接调用。Atomics 的所有属性和方法都是静态的(与 Math 对象一样)。
多个共享内存的线程能够同时读写同一位置上的数据。原子操作会确保正在读或写的数据的值是符合预期的,即下一个原子操作一定会在上一个原子操作结束后才会开始,其操作过程不会中断。
wait() 和 wake() 方法采用的是 Linux 上的 futexes 模型(fast user-space mutex,快速用户空间互斥量),可以让进程一直等待直到某个特定的条件为真,主要用于实现阻塞。
1.异步迭代
在async/await的某些时刻,你可能尝试在同步循环中调用异步函数。例如:
这段代码不会正常运行,下面这段同样也不会:
这段代码中,循环本身依旧保持同步,并在在内部异步函数之前全部调用完成。
ES2018引入异步迭代器(asynchronous iterators),这就像常规迭代器,除了next()方法返回一个Promise。因此await可以和for...of循环一起使用,以串行的方式运行异步操作。例如:
2.Promise.finally()
一个Promise调用链要么成功到达最后一个.then(),要么失败触发.catch()。在某些情况下,你想要在无论Promise运行成功还是失败,运行相同的代码,例如清除,删除对话,关闭数据库连接等。
.finally()允许你指定最终的逻辑:
3.Rest/Spread 属性
ES2015引入了Rest参数和扩展运算符。三个点(...)仅用于数组。Rest参数语法允许我们将一个不定数量的参数表示为一个数组。
展开操作符以相反的方式工作,将数组转换成可传递给函数的单独参数。例如Math.max()返回给定数字中的最大值:
ES2018为对象解构提供了和数组一样的Rest参数()和展开操作符,一个简单的例子:
或者你可以使用它给函数传递参数:
扩展运算符可以在其他对象内使用,例如:
可以使用扩展运算符拷贝一个对象,像是这样obj2 = ...obj1,但是 这只是一个对象的浅拷贝 。另外,如果一个对象A的属性是对象B,那么在克隆后的对象cloneB中,该属性指向对象B。
4.正则表达式命名捕获组
JavaScript正则表达式可以返回一个匹配的对象——一个包含匹配字符串的类数组,例如:以YYYY-MM-DD的格式解析日期:
这样的代码很难读懂,并且改变正则表达式的结构有可能改变匹配对象的索引。
ES2018允许命名捕获组使用符号?<name>,在打开捕获括号(后立即命名,示例如下:
任何匹配失败的命名组都将返回undefined。
命名捕获也可以使用在replace()方法中。例如将日期转换为美国的 MM-DD-YYYY 格式:
5.正则表达式反向断言
目前JavaScript在正则表达式中支持先行断言(lookahead)。这意味着匹配会发生,但不会有任何捕获,并且断言没有包含在整个匹配字段中。例如从价格中捕获货币符号:
ES2018引入以相同方式工作但是匹配前面的反向断言(lookbehind),这样我就可以忽略货币符号,单纯的捕获价格的数字:
以上是 肯定反向断言 ,非数字\\D必须存在。同样的,还存在 否定反向断言 ,表示一个值必须不存在,例如:
6.正则表达式dotAll模式
正则表达式中点.匹配除回车外的任何单字符,标记s改变这种行为,允许行终止符的出现,例如:
7.正则表达式 Unicode 转义
到目前为止,在正则表达式中本地访问 Unicode 字符属性是不被允许的。ES2018添加了 Unicode 属性转义——形式为\\p...和\\P...,在正则表达式中使用标记 u (unicode) 设置,在\\p块儿内,可以以键值对的方式设置需要匹配的属性而非具体内容。例如:
此特性可以避免使用特定 Unicode 区间来进行内容类型判断,提升可读性和可维护性。
8.非转义序列的模板字符串
之前,\\\\u开始一个 unicode 转义,\\\\x开始一个十六进制转义,\\后跟一个数字开始一个八进制转义。这使得创建特定的字符串变得不可能,例如Windows文件路径 C:\\\\uuu\\\\xxx\\111。更多细节参考模板字符串。
1.行分隔符(U + 2028)和段分隔符(U + 2029)符号现在允许在字符串文字中,与JSON匹配
以前,这些符号在字符串文字中被视为行终止符,因此使用它们会导致SyntaxError异常。
2.更加友好的 JSON.stringify
如果输入 Unicode 格式但是超出范围的字符,在原先JSON.stringify返回格式错误的Unicode字符串。现在实现了一个改变JSON.stringify的第3阶段提案,因此它为其输出转义序列,使其成为有效Unicode(并以UTF-8表示)
3.新增了Array的flat()方法和flatMap()方法
flat()和flatMap()本质上就是是归纳(reduce) 与 合并(concat)的操作。
Array.prototype.flat()
flat() 方法会按照一个可指定的深度递归遍历数组,并将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
Array.prototype.flatMap()
flatMap() 方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap 通常在合并成一种方法的效率稍微高一些。 这里我们拿map方法与flatMap方法做一个比较。
4.新增了String的trimStart()方法和trimEnd()方法
5.Object.fromEntries()
Object.entries()方法的作用是返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for...in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环也枚举原型链中的属性)。
而Object.fromEntries() 则是 Object.entries() 的反转。
Object.fromEntries() 函数传入一个键值对的列表,并返回一个带有这些键值对的新对象。这个迭代参数应该是一个能够实现@iterator方法的的对象,返回一个迭代器对象。它生成一个具有两个元素的类似数组的对象,第一个元素是将用作属性键的值,第二个元素是与该属性键关联的值。
6.Symbol.prototype.description
通过工厂函数Symbol()创建符号时,您可以选择通过参数提供字符串作为描述:
以前,访问描述的唯一方法是将符号转换为字符串:
现在引入了getter Symbol.prototype.description以直接访问描述:
7.String.prototype.matchAll
matchAll() 方法返回一个包含所有匹配正则表达式及分组捕获结果的迭代器。 在 matchAll 出现之前,通过在循环中调用regexp.exec来获取所有匹配项信息(regexp需使用/g标志:
如果使用matchAll ,就可以不必使用while循环加exec方式(且正则表达式需使用/g标志)。使用matchAll 会得到一个迭代器的返回值,配合 for...of, array spread, or Array.from() 可以更方便实现功能:
matchAll可以更好的用于分组
8.Function.prototype.toString()现在返回精确字符,包括空格和注释
9.修改 catch 绑定
在 ES10 之前,我们必须通过语法为 catch 子句绑定异常变量,无论是否有必要。很多时候 catch 块是多余的。 ES10 提案使我们能够简单的把变量省略掉。
不算大的改动。
之前是
现在是
10.新的基本数据类型BigInt
现在的基本数据类型(值类型)不止5种(ES6之后是六种)了哦!加上BigInt一共有七种基本数据类型,分别是: String、Number、Boolean、Null、Undefined、Symbol、BigInt
ES11新特性
写在前面
ES2020(即 ES11)上周(2020 年 6 月)已经正式发布,在此之前进入 Stage 4 的 10 项提案均已纳入规范,成为 JavaScript 语言的新特性
一.特性一览
ES Module 迎来了一些增强:
-
import():一种可以用动态模块标识异步引入模块的的语法
-
import.meta:一个对象,用来携带模块相关的元信息
-
export * as ns from “mod”;:一种新的聚合导出语法
正式支持了安全的链式操作:
-
Optional chaining:新运算符
?.
能够在属性访问、方法调用前检查其是否存在 -
Nullish coalescing Operator:用来提供默认值的新运算符
??
提供了大数运算的原生支持:
-
BigInt – arbitrary precision integers:一种新的基础数值类型,支持任意精度的整数运算
一些基础 API 也有了新的变化:
-
Promise.allSettled:一个新的 Promise 组合器,不像
all
、race
一样具有短路特性 -
String.prototype.matchAll:以迭代器的形式返回全局匹配模式下的正则表达式匹配到的所有结果(
index
、groups
等) -
globalThis:访问全局作用域
this
的通用方法 -
for-in mechanics:规范
for-in
循环的某些行为
二.ES Module 增强
动态 import
我们知道ES Module是一套静态的模块系统:
The existing syntactic forms for importing modules are static declarations.
静态体现在:
They accept a string literal as the module specifier, and introduce bindings into the local scope via a pre-runtime “linking” process.
-
静态加载:
import/export
声明只能出现在顶层作用域,不支持按需加载、懒加载 -
静态标识:模块标识只能是字符串字面量,不支持运行时动态计算而来的模块名
例如:
if (Math.random()) {
import ‘foo‘; // SyntaxError
}
// You can’t even nest `import` and `export`
// inside a simple block:
{
import ‘foo‘; // SyntaxError
}
这种严格的静态模块机制让基于源码的静态分析、编译优化有了更大的发挥空间:
This is a great design for the 90% case, and supports important use cases such as static analysis, bundling tools, and tree shaking.
但对另一些场景很不友好,比如:
-
苛求首屏性能的场景:通过
import
声明引用的所有模块(包括初始化暂时用不到的模块)都会在初始化阶段前置加载,影响首屏性能 -
难以提前确定目标模块标识的场景:例如根据用户的语言选项动态加载不同的模块(
module-en
、module-zh
等) -
仅在特殊情况下才需要加载某些模块的场景:例如异常情况下加载降级模块
为了满足这些需要动态加载模块的场景,ES2020 推出了动态 import 特性(import()
):
import(specifier)
import()
“函数”输入模块标识specifier
(其解析规则与import
声明相同),输出Promise
,例如:
// 目标模块 ./lib/my-math.js
function times(a, b) {
return a * b;
}
export function square(x) {
return times(x, x);
}
export const LIGHTSPEED = 299792458;
// 当前模块 index.js
const dir = ‘./lib/‘;
const moduleSpecifier = dir + ‘my-math.mjs‘;
async function loadConstant() {
const myMath = await import(moduleSpecifier);
const result = myMath.LIGHTSPEED;
assert.equal(result, 299792458);
return result;
}
// 或者不用 async & await
function loadConstant() {
return import(moduleSpecifier)
.then(myMath => {
const result = myMath.LIGHTSPEED;
assert.equal(result, 299792458);
return result;
});
}
与import
声明相比,import()
特点如下:
-
能够在函数、分支等非顶层作用域使用,按需加载、懒加载都不是问题
-
模块标识支持变量传入,可动态计算确定模块标识
-
不仅限于
module
,在普通的script
中也能使用
注意,虽然长的像函数,但import()
实际上是个操作符,因为操作符能够携带当前模块相关信息(用来解析模块表示),而函数不能:
Even though it works much like a function,
import()
is an operator: in order to resolve module specifiers relatively to the current module, it needs to know from which module it is invoked. A normal function cannot receive this information as implicitly as an operator can. It would need, for example, a parameter.
import.meta
另一个 ES Module 新特性是import.meta
,用来透出模块特定的元信息:
import.meta, a host-populated object available in Modules that may contain contextual information about the Module.
比如:
-
模块的 URL 或文件名:例如 Node.js 里的
__dirname
、__filename
-
所处的
script
标签:例如浏览器支持的document.currentScript
-
入口模块:例如 Node.js 里的
process.mainModule
诸如此类的元信息都可以挂到import.meta
属性上,例如:
// 模块的 URL(浏览器环境)
import.meta.url
// 当前模块所处的 script 标签
import.meta.scriptElement
但需要注意的是,规范并没有明确定义具体的属性名和含义,都由具体实现来定,所以特性提案里的希望浏览器支持的这两个属性将来可能支持也可能不支持
P.S.import.meta
本身是个对象,原型为null
export-ns-from
第三个 ES Module 相关的新特性是另一种模块导出语法:
export * as ns from "mod";
同属于export ... from ...
形式的聚合导出,作用上类似于:
import * as ns from "mod";
export {ns};
但不会在当前模块作用域引入目标模块的各个 API 变量
P.S.对照import * as ns from "mod";
语法,看起来像是 ES6 模块设计中排列组合的一个疏漏;)
三.链式操作支持
Optional Chaining
相当实用的一个特性,用来替代诸如此类冗长的安全链式操作:
const street = user && user.address && user.address.street;
可换用新特性(?.
):
const street = user?.address?.street;
语法格式如下:
obj?.prop // 访问可选的静态属性
// 等价于
(obj !== undefined && obj !== null) ? obj.prop : undefined
obj?.[«expr»] // 访问可选的动态属性
// 等价于
(obj !== undefined && obj !== null) ? obj[«expr»] : undefined
func?.(«arg0», «arg1») // 调用可选的函数或方法
// 等价于
(func !== undefined && func !== null) ? func(arg0, arg1) : undefined
P.S.注意操作符是?.
而不是单?
,在函数调用中有些奇怪alert?.()
,这是为了与三目运算符中的?
区分开
机制非常简单,如果出现在问号前的值不是undefined
或null
,才执行问号后的操作,否则返回undefined
同样具有短路特性:
// 在 .b?.m 时短路返回了 undefined,而不会 alert ‘here‘
({a: 1})?.a?.b?.m?.(alert(‘here‘))
与&&
相比,新的?.
操作符更适合安全进行链式操作的场景,因为:
-
语义更明确:
?.
遇到属性/方法不存在就返回undefined
,而不像&&
一样返回左侧的值(几乎没什么用) -
存在性判断更准确:
?.
只针对null
和undefined
,而&&
遇到任意假值都会返回,有时无法满足需要
例如常用的正则提取目标串,语法描述相当简洁:
‘string‘.match(/(sing)/)?.[1] // undefined
// 之前需要这样做
(‘string‘.match(/(sing)/) || [])[1] // undefined
还可以配合 Nullish coalescing Operator 特性填充默认值:
‘string‘.match(/(sing)/)?.[1] ?? ‘‘ // ‘‘
// 之前需要这样做
(‘string‘.match(/(sing)/) || [])[1] || ‘‘ // ‘‘
// 或者
(‘string‘.match(/(sing)/) || [, ‘‘])[1] // ‘‘
Nullish coalescing Operator
同样引入了一种新的语法结构(??
):
actualValue ?? defaultValue
// 等价于
actualValue !== undefined && actualValue !== null ? actualValue : defaultValue
用来提供默认值,当左侧的actualValue
为undefined
或null
时,返回右侧的defaultValue
,否则返回左侧actualValue
类似于||
,主要区别在于??
只针对null
和undefined
,而||
遇到任一假值都会返回右侧的默认值
四.大数运算
新增了一种基础类型,叫BigInt
,提供大整数运算支持:
BigInt is a new primitive that provides a way to represent whole numbers larger than 2^53, which is the largest number Javascript can reliably represent with the Number primitive.
BigInt
JavaScript 中Number
类型所能准确表示的最大整数是2^53
,不支持对更大的数进行运算:
const x = Number.MAX_SAFE_INTEGER;
// 9007199254740991 即 2^53 - 1
const y = x + 1;
// 9007199254740992 正确
const z = x + 2
// 9007199254740992 错了,没变
P.S.至于为什么是 2 的 53 次方,是因为 JS 中数值都以 64 位浮点数形式存放,刨去 1 个符号位,11 个指数位(科学计数法中的指数),剩余的 52 位用来存放数值,2 的 53 次方对应的这 52 位全部为 0,能表示的下一个数是2^53 + 2
,中间的2^53 + 1
无法表示:
JavaScript Max Safe Integer
具体解释见BigInts in JavaScript: A case study in TC39
BigInt
类型的出现正是为了解决此类问题:
9007199254740991n + 2n
// 9007199254740993n 正确
引入的新东西包括:
-
大整数字面量:给数字后缀一个
n
表示大整数,例如9007199254740993n
、0xFFn
(二进制、八进制、十进制、十六进制字面量通通可以后缀个n
变成BigInt
) -
bigint
基础类型:typeof 1n === ‘bigint‘
-
类型构造函数:
BigInt
-
重载数学运算符(加减乘除等):支持大整数运算
例如:
// 创建一个 BigInt
9007199254740993n
// 或者
BigInt(9007199254740993)
// 乘法运算
9007199254740993n * 2n
// 幂运算
9007199254740993n ** 2n
// 比较运算
0n === 0 // false
0n === 0n // true
// toString
123n.toString() === ‘123‘
P.S.关于 BigInt API 细节的更多信息,见ECMAScript feature: BigInt – arbitrary precision integers
需要注意的是BigInt
不能与Number
混用进行运算:
9007199254740993n * 2
// 报错 Uncaught TypeError: Cannot mix BigInt and other types, use explicit conversions
并且BigInt
只能表示整数,所以除法直接取整(相当于Math.trunc()
):
3n / 2n === 1n
五.基础 API
基础 API 也有一些新的变化,包括 Promise、字符串正则匹配、for-in
循环等
Promise.allSettled
继Promise.all、Promise.race之后,Promise
新增了一个静态方法叫allSettled
:
// 传入的所有 promise 都有结果(从 pending 状态变成 fulfilled 或 rejected)之后,触发 onFulfilled
Promise.allSettled([promise1, promise2]).then(onFulfilled);
P.S.另外,any
也在路上了,目前(2020/6/21)处于 Stage 3
类似于all
,但不会因为某些项rejected
而短路,也就是说,allSettled
会等到所有项都有结果(无论成功失败)后才进入Promise
链的下一环(所以它一定会变成 Fulfilled 状态):
A common use case for this combinator is wanting to take an action after multiple requests have completed, regardless of their success or failure.
例如:
Promise.allSettled([Promise.reject(‘No way‘), Promise.resolve(‘Here‘)])
.then(results => {
console.log(results);
// [
// {status: "rejected", reason: "No way"},
// {status: "fulfilled", value: "Here"}
// ]
}, error => {
// No error can get here!
})
String.prototype.matchAll
字符串处理的一个常见场景是想要匹配出字符串中的所有目标子串,例如:
const str = ‘es2015/es6 es2016/es7 es2020/es11‘;
str.match(/(esd+)/es(d+)/g)
// 顺利得到 ["es2015/es6", "es2016/es7", "es2020/es11"]
match()
方法中,正则表达式所匹配到的多个结果会被打包成数组返回,但无法得知每个匹配除结果之外的相关信息,比如捕获到的子串,匹配到的index
位置等:
This is a bit of a messy way to obtain the desired information on all matches.
此时只能求助于最强大的exec
:
const str = ‘es2015/es6 es2016/es7 es2020/es11‘;
const reg = /(esd+)/es(d+)/g;
let matched;
let formatted = [];
while (matched = reg.exec(str)) {
formatted.push(`${matched[1]} alias v${matched[2]}`);
}
console.log(formatted);
// 得到 ["es2015 alias v6", "es2016 alias v7", "es2020 alias v11"]
而 ES2020 新增的matchAll()
方法就是针对此类种场景的补充:
const results = ‘es2015/es6 es2016/es7 es2020/es11‘.matchAll(/(esd+)/es(d+)/g);
// 转数组处理
Array.from(results).map(r => `${r[1]} alias v${r[2]}`);
// 或者从迭代器中取出直接处理
// for (const matched of results) {}
// 得到结果同上
注意,matchAll()
不像match()
一样返回数组,而是返回一个迭代器,对大数据量的场景更友好
for-in 遍历机制
JavaScript 中通过for-in
遍历对象时 key 的顺序是不确定的,因为规范没有明确定义,并且能够遍历原型属性让for-in
的实现机制变得相当复杂,不同 JavaScript 引擎有各自根深蒂固的不同实现,很难统一
-
所以 ES2020 不要求统一属性遍历顺序,而是对遍历过程中的一些特殊 Case 明确定义了一些规则:
-
遍历不到 Symbol 类型的属性
-
遍历过程中,目标对象的属性能被删除,忽略掉尚未遍历到却已经被删掉的属性
-
遍历过程中,如果有新增属性,不保证新的属性能被当次遍历处理到
-
属性名不会重复出现(一个属性名最多出现一次)
-
目标对象整条原型链上的属性都能遍历到
具体见13.7.5.15 EnumerateObjectProperties
globalThis
最后一个新特性是globalThis
,用来解决浏览器,Node.js 等不同环境下,全局对象名称不统一,获取全局对象比较麻烦的问题:
var getGlobal = function () {
// the only reliable means to get the global object is
// `Function(‘return this‘)()`
// However, this causes CSP violations in Chrome apps.
if (typeof self !== ‘undefined‘) { return self; }
if (typeof window !== ‘undefined‘) { return window; }
if (typeof global !== ‘undefined‘) { return global; }
throw new Error(‘unable to locate global object‘);
};
globalThis
作为统一的全局对象访问方式,总是指向全局作用域中的this
值:
The global variable globalThis is the new standard way of accessing the global object. It got its name from the fact that it has the same value as this in global scope.
P.S.为什么不叫global
?是因为global
可能会影响现有的一些代码,所以另起一个globalThis
避免冲突
至此,ES2020 的所有新特性都清楚了
六.总结
比起ES2019,ES2020 算是一波大更新了,动态 import、安全的链式操作、大整数支持……全都加入了豪华午餐
参考资料
-
ECMAScript® 2020 Language Specification
以上是关于ES6、ES7、ES8、ES9、ES10新特性一览的主要内容,如果未能解决你的问题,请参考以下文章