一.概览
2个主特性:
Asynchronous Iteration
Rest/Spread Properties
正则表达式相关的4个小特性:
RegExp Lookbehind Assertions
RegExp Unicode Property Escapes
RegExp named capture groups
s (dotAll) flag for regular expressions
其它:
二.Asynchronous Iteration
普通的(同步)迭代是类似这样的:
或者通过循环去遍历:
但如果数据源是异步的,循环就只能拿到一堆Promise,而不是想要的值:
这是因为ES2015推出的Iterator接口仅适用于同步数据源:
Iterators are only suitable for representing synchronous data sources
为了支持异步数据源,ES2018新增了3个东西:
异步迭代器接口:AsyncIterator
异步迭代语句:
异步(迭代器的)生成器:async generator functions
async generator用来快速实现AsyncIterator接口,而实现了AsyncIterator接口的东西就能够方便地通过遍历了
AsyncIterator
类似于同步Iterator接口:
异步AsyncIterator接口要求返回携带着的Promsie:
接口对应的方法名为,例如:
试玩:
P.S.同步Iterator接口对应的方法名为,具体见for…of循环_ES6笔记1 | 2.不能遍历对象
for-await-of
类似的,实现了AsyncIterator接口的,就叫async iterable,就有能通过遍历的特权:
用起来与同步没太大区别,只是实现AsyncIterator接口有些麻烦,迫切需要一种更方便的方式
P.S.同样,关键字只能出现在 function里,的也不例外
async generator
async generator就是我们迫切想要的异步迭代器的生成器:
方便多了,更进一步地,async generator返回值本来就是async iterable(隐式实现了AsyncIterator接口),没必要手动实现该接口:
类似于同步版本:
就具体语法而言,async generator有3个特点:
返回async iterable对象,其、、方法都返回Promise,而不直接返回,并且会默认实现方法(因此async generator返回async iterable)
函数体中允许出现、语句
同样支持拼接迭代器
例如:
P.S.注意一个细节,类似于,也能接受非Promise值(同步值)
P.S.另外,async generator里的等价于,具体见Suggestion: Make uncatchable
实现原理
Implicit in the concept of the async iterator is the concept of a request queue. Since iterator methods may be called many times before the result of a prior request is resolved, each method call must be queued internally until all previous request operations have completed.
asyncIterator内部维持了一个请求队列,以此保证遍历次序,例如:
输出:
第一个结果还没完成,立即发起的第二个,会被记到队列里,等到前置都完成以后,才实际去做
上例相当于:
P.S.关于请求队列机制的更多信息,请查看ES2018: asynchronous iteration | await in async generators
三.Rest/Spread Properties
ES2015里推出了3种的语法:
不定参数
剩余元素
展开元素
例如:
ES2018新增了两种:
剩余属性
展开属性
剩余属性
基本用法如下:
嵌套结构同样适用:
常见的应用场景:
需要特别注意,解构赋值与剩余属性的差异:
这两种方式看似等价,实则不然:
关键区别在于剩余属性只取自身属性,而解构赋值会取自身及原型链上的属性,所以对照组中的变成了(拿不到原型属性,仅拿到了实例属性)
展开属性
基本用法示例:
常见应用场景:
P.S.关于打包-还原的实际应用,见react-redux源码解读 | 默认参数与对象解构
另外,还有2个细节:
展开属性只触发(待展开对象的)getter,不触发(目标对象的)setter
尝试展开不会引发报错,而是忽略掉
例如:
四.正则表达式增强
说来话长,1999年ES3引入正则表达式支持,2016年的ES2015增强过一波:
2017年的ES2018进一步增强:
s (dotAll) flag for regular expressions:点号通配模式,在此模式下,点号可以匹配任意字符(默认点号只能匹配除换行符外的任意字符)
RegExp Lookbehind Assertions:肯定逆序环视,支持向后看
RegExp named capture groups:命名捕获分组
RegExp Unicode Property Escapes:Unicode(序列)属性转义
s (dotAll) flag for regular expressions
不开模式的话,(点号)能够匹配除换行外的任意字符,换行符有4个:
U+000A LINE FEED (LF)
U+000D CARRIAGE RETURN (CR)
U+2028 LINE SEPARATOR:行分隔符
U+2029 PARAGRAPH SEPARATOR:段分隔符(与行分隔符一样,都是不可见字符)
例如:
要想匹配任意字符的话,只能通过一些技巧绕过,如:
有了点号通配模式以后,这些换行符都能被点号匹配(像其它语言的正则引擎一样):
另外,还有两个属性用来获取该模式是否已开启:
注意,点号通配模式()并不影响多行匹配模式(),二者是完全独立的:
:只影响(点号)的匹配行为
:只影响的匹配行为
可以一起用,也互不干扰:
P.S.模式术语叫增强的行锚点模式(具体见正则表达式学习笔记 | 九.附表【元字符表】【模式控制符表】【特殊元字符表】):
增强的行锚点模式,把段落分割成逻辑行,使得^和$可以匹配每一行的相应位置,而不是整个串的开始和结束位置
RegExp Lookbehind Assertions
正则环视(lookaround)相关的一个特性,环视的特点是不匹配任何字符,只匹配文本中的特定位置:
Lookarounds are zero-width assertions that match a string without consuming anything.
ES2018引入了逆序环视:
:肯定逆序环视(Positive lookbehind assertions),子表达式能够匹配左侧文本时才成功匹配
:否定逆序环视(Negative lookbehind assertions),子表达式不能匹配左侧文本时才成功匹配
一种向后看的能力,典型应用场景如下:
向前看的能力一直都有,例如:
具体见ES5规范15.10.2.8 Atom中的NOTE 2与NOTE 3
逆序环视与反向引用
实现上,含逆序环视的正则表达式的匹配顺序是从右向左的,例如:
从上例能够发现另一个细节:虽然扫描顺序相反,但捕获分组排序都是从左向右的
此外,逆序环视场景下反向扫描对反向引用有影响,毕竟只能引用已匹配过的内容:
Within a backreference, it is only possible to refer to captured groups that have already been evaluated.
所以要想匹配叠词的话,应该这样做:
而不是:
实际上,这里的什么都匹配不到,永远是空串(因为从右向左扫,还没捕获哪来的引用),删掉它也没关系()
P.S.关于反向引用与逆序环视的更多信息,见Greediness proceeds from right to left
RegExp named capture groups
常见的日期格式转换场景:
我们通过来引用对应的捕获到的内容,存在两个问题:
可读性:仅表示第几个捕获分组,不含其它语义
灵活性:一旦正则表达式中括号顺序发生变化,replacement()要跟着变
命名捕获分组能够很好的解决这两个问题:
正则表达式中的捕获分组与replacement中的引用都有了额外语义
另外,匹配结果对象身上也有一份命名捕获内容:
从语法上看,引入了3个新东西:
:命名捕获型括号
:命名反向引用
:命名replacement引用,函数形式的replacement把作为最后一个参数,具体见Replacement targets
例如:
P.S.特性不错,语法有点太长了啊,对比与。。。虽说是出于向后兼容考虑
RegExp Unicode Property Escapes
Unicode字符有一些属性,比如是希腊文字,在Unicode中对应的属性是
为了支持根据Unicode属性特征匹配字符的场景,提供了两种语法:
:匹配一个Unicode属性名等于指定属性值的字符
:匹配一个该Unicode属性值为true的字符
P.S.对应的表示补集
注意,都要开模式,不开不认
前者适用于非布尔值(non-binary)属性,后者用于布尔值(binary)属性,例如:
P.S.支持的属性名及值都按Unicode标准来,定义在PropertyAliases.txt、ropertyValueAliases.txt,布尔值属性定义在UTS18 RL1.2
喜报,Emoji问题也终于有终极解决方案了:
P.S.关于binary为什么表示布尔值属性,见1.2 Properties
P.S.Unicode字符表见
五.其它小特性
的Promise要么要么,而有些时候需要的是,比如只想等到异步操作结束,不论成功失败,此时就是最合适的解决方案:
可以在块里做一些清理工作(类似于的),比如隐藏loading、关闭文件描述符、log记录操作已完成
之前类似的场景一般通过来解决,但的特点在于:
没有参数(专职清理,不关心参数)
不论还是都触发
不影响Promise链的状态及结果(而会得到),除非块里的或者会让Promise链变为
例如:
始终没被改变,因为从设计上不希望影响返回值:
Syntactic finally can only modify the return value with an “abrupt completion”: either throwing an exception, or returning a value early. Promise#finally will not be able to modify the return value, except by creating an abrupt completion by throwing an exception (ie, rejecting the promise)
其中,指的是返回Rejected Promise,例如:
Lifting template literal restriction
模板字符串默认识别(尝试去匹配解释)其中的转义字符:
:Unicode字符序列,如或
:十六进制数值,如
:八进制,如,具体见Octal escape sequences
P.S.实际上,八进制转义序列在模板字面量和严格模式下的字符串字面量都是不合法的:
Octal escapes are forbidden in template literals and strict mode string literals.
对于不合法的转义序列,会报错:
但是,模板字符串作为ES2015最开放的特性:
标签模板以开放的姿态欢迎库设计者们来创建强有力领域特定语言。这些语言可能看起来不像JS,但是它们仍可以无缝嵌入到JS中并与JS的其它语言特性智能交互。我不知道这一特性将会带领们走向何方,但它蕴藏着无限的可能性,这令我感到异常兴奋!
这种粗暴的默认解析实际上限制了模板字符串的包容能力,例如latex:
这是一段合法的latex源码,但其中的、和会引发报错
针对这个问题,ES2018决定对标签模板去掉这层默认解析,把处理非法转义序列的工作抛到上层:
Remove the restriction on escape sequences.
Lifting the restriction raises the question of how to handle cooked template values that contain illegal escape sequences.
例如:
P.S.关于标签模板的更多信息,请查看模板字符串_ES6笔记3
注意,这个特性仅针对标签模板,普通模板字符串仍然保留之前的行为(遇到非法转义序列会报错):
六.总结
最实在的特性要数正则表达式相关增强,此外Promise任务模型正在逐步完善、generator与async function擦出了火花、已经广泛应用的展开运算符终于敲定了、模板字符串的包容性限制去掉了一些,使之符合设计初衷
总之,有点着急的JS语言正在往好的方向发展
P.S.ES2019相关信息,见Finished Proposals
参考资料
ECMAScript regular expressions are getting better!
Template Literal Revision
ECMAScript regular expressions are getting better!
联系ayqy
领取专属 10元无门槛券
私享最新 技术干货