说明
记录 ES2018 相关知识
异步迭代
- 通过
Symbol.asyncIterator
访问内建的迭代器
- 方法
next()
返回的是 Promise
1 2 3 4 5 6 7
| function wait(sec) { return new Promise(function(resolve, reject) { setTimeout(() => { resolve(sec) }, sec * 1000) }) }
|
for await…of
1 2 3 4 5 6 7 8 9 10 11
| async function doFun() { for await (const res of [wait(2), wait(4)]) { console.log(res) } }
doFun()
|
for-await-of
不能在模块最顶层使用,同 await 要在 async 函数内使用
1 2 3 4 5 6 7 8 9 10 11
| async function doFun2() { for await (const res of [wait(4), wait(2)]) { console.log(res) } }
doFun2()
|
1 2 3 4 5 6 7 8 9 10 11
| async function doFun3() { for (const x of await Promise.all([wait(2), wait(4)])) { console.log(x) } }
doFun3()
|
异步 generator 函数
- 异步 Generator 函数的返回值是一个异步 Iterator,即每次调用它的
next
方法,会返回一个 Promise 对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| async function* asyncGenerator() { console.log('Start') const result = await wait(2) yield 'Result: ' + result console.log('Done') }
const ag = asyncGenerator() ag.next().then(({ value, done }) => { console.log(value) })
|
抛出错误
1 2 3 4 5 6 7 8
| async function* asyncGenerator() { throw new Error('Problem!') }
asyncGenerator() .next() .catch(err => console.log(err))
|
解构
rest 与对象解构
1 2 3
| const obj = { foo: 1, bar: 2, baz: 3 } const { foo, ...rest } = obj console.log(rest)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const obj = { foo: { a: 1, b: 2, c: 3 }, bar: 4, baz: 5 } const { foo: { a, ...rest1 }, ...rest2 } = obj
console.log(rest1) console.log(rest2)
|
1 2
| const { ...rest, foo } = obj const { foo, ...rest1, ...rest2 } = obj
|
对象解构 vs. Object.assign()
Object.assign()
触发源对象的 set
方法,解构则不会
1 2 3 4 5 6
| Object.defineProperty(Object.prototype, 'foo', { set(value) { console.log('SET', value) } }) const obj = { foo: 123 }
|
1 2 3 4 5 6
| 'use strict' Object.defineProperty(Object.prototype, 'bar', { writable: false, value: 'abc' })
|
1 2 3
| const tmp = {} tmp.bar = 123
|
1
| const obj = { bar: 123 }
|
1 2 3 4
| const obj = { bar: 123 }
Object.assign({}, obj)
|
1 2 3 4
| const obj = { bar: 123 }
const obj2 = { ...obj } console.log(obj2)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const proto = { inheritedEnumerable: 1 } const obj = Object.create(proto, { ownEnumerable: { value: 2, enumerable: true }, ownNonEnumerable: { value: 3, enumerable: false } })
const res1 = { ...obj } console.log(res1)
const res2 = Object.assign({}, obj) console.log(res2)
|
正则表达式
命名捕获组(Named capture groups)
1 2 3 4 5 6
| const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/
const matchObj = RE_DATE.exec('1999-12-31') const year = matchObj.groups.year const month = matchObj.groups.month const day = matchObj.groups.day
|
- Named capture groups 同样可以通过下标获取组
1 2 3
| const year2 = matchObj[1] const month2 = matchObj[2] const day2 = matchObj[3]
|
反向引用(Backreferences)
\k<name>
在正则表达式中表示:匹配先前捕获过的同名捕获组字符串
1 2 3
| const RE_TWICE = /^(?<word>[a-z]+)!\k<word>$/ RE_TWICE.test('abc!abc') RE_TWICE.test('abc!ab')
|
编号捕获组(numbered capture groups )的反向引用语法 也适用于 命名捕获组
1 2 3
| const RE_TWICE = /^(?<word>[a-z]+)!\1$/ RE_TWICE.test('abc!abc') RE_TWICE.test('abc!ab')
|
两者也可以混合使用
1 2 3
| const RE_TWICE = /^(?<word>[a-z]+)!\k<word>!\1$/ RE_TWICE.test('abc!abc!abc') RE_TWICE.test('abc!abc!ab')
|
replace() 和命名捕获组
1 2 3
| const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/ console.log('1999-12-31'.replace(RE_DATE, '$<month>/$<day>/$<year>'))
|
1 2 3 4 5 6 7 8
| const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/ console.log( '1999-12-31'.replace( RE_DATE, (g0, y, m, d, offset, input, { year, month, day }) => month + '/' + day + '/' + year ) )
|
g0
包含匹配得到的整个字符串 1999-12-31
y, m, d
指捕获组 1-3,也是命名组 year, month, day
offset
指匹配到的内容在字符串中的位置
input
指输入的整个字符串
- 最后的一个参数是新增的,包含命名捕获组,这里是
year, month, day
1 2 3 4 5 6 7 8 9 10 11
| const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/
console.log( '1999-12-31'.replace(RE_DATE, (...args) => { const { year, month, day } = args[args.length - 1] return month + '/' + day + '/' + year }) )
|
未匹配的命名组
1 2 3 4 5 6 7 8
| const RE_OPT_A = /^(?<as>a+)?$/ const matchObj = RE_OPT_A.exec('')
console.log(matchObj.groups.as === undefined)
console.log('as' in matchObj.groups)
|
正则表达式 Unicode 转义属性
正则表达式中使用 u 标志(flags),之后在\p{}
花括号内声明 Unicode 字符属性
1 2 3 4 5 6 7
| /^\p{White_Space}+$/u.test('\t \n\r')
/^\p{Script=Greek}+$/u.test('μετά')
|
WIKI - Unicode 字符属性
正则表达式 - lookbehind assertion
翻译成:反向断言?回顾断言?翻译成反向断言算了,重在理解。
符号 |
翻译 |
含义 |
(?<=exp)reg |
反向肯定断言 |
reg 匹配的内容前面内容满足 exp 规则 |
(?<!exp)reg |
反向否定断言 |
reg 匹配的内容前面内容不满足 exp 规则 |
- 正向断言(lookahead assertions)
符号 |
翻译 |
含义 |
reg(?=exp) |
正向肯定断言 |
reg 匹配的内容后面内容满足 exp 规则 |
reg(?!exp) |
正向否定断言 |
reg 匹配的内容后面内容不满足 exp 规则 |
正向断言和反向断言均不捕获分组。
eg
- 正向断言
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const RE_AS_BS = /aa(?=bb)/ const match1 = RE_AS_BS.exec('aabb') match1[0]
const match2 = RE_AS_BS.exec('aab') match2
const RE_AS_NO_BS = /aa(?!bb)/ RE_AS_NO_BS.test('aabb')
RE_AS_NO_BS.test('aab')
|
- 反向断言
1 2 3 4 5 6 7 8
| 'a1ba2ba3b'.match(/(?<=b)a.b/g)
const RE_NO_DOLLAR_PREFIX = /(?<!\$)foo/g '$foo %foo foo'.replace(RE_NO_DOLLAR_PREFIX, 'bar')
|
正则表达式 s(dotAll) 标志符
- 添加
s
标志符的作用:使正则表达式中的 .
匹配包括行终止符在内的所有单字符。不添加的话不匹配行终止符。
1 2 3 4 5
| /^.$/.test('\n')
/^.$/s.test('\n')
|
行终止符包括\n
, \r
等。ECMA-line terminators
- 标志符
/s
对应的正则实例属性是 dotAll
1 2 3 4 5
| /./s.dotAll
/./g.global
|
Promise.finally()
- then’s callback is only executed if
promise
is fulfilled
- catch’s callback is only executed if
promise
is rejected. Or then’s callback throws an exception or returns a rejected Promise.
- 不管 fulfilled 或 rejected, finally 都会执行
1 2 3 4
| promise .then(result => {···}) .catch(error => {···}) .finally(() => {···})
|
模板字符串
带标签的模板字面量及转义序列
自 ES2016 起,带标签的模版字面量遵守以下转义序列的规则:
- Unicode 字符以
"\u"
开头,例如\u00A9
- Unicode 码位用
"\u{}"
表示,例如\u{2F804}
- 十六进制以
"\x"
开头,例如\xA9
- 八进制以
"\"
和数字开头,例如\251
ES2018 关于非法转义序列的修订
移除对 ECMAScript 在带标签的模版字符串中转义序列的语法限制。不过,非法转义序列在”cooked”当中仍然会体现出来。它们将以 undefined 元素的形式存在于”cooked”之中:
1 2 3 4 5 6 7
| function latex(str) { return { cooked: str[0], raw: str.raw[0] } }
console.log(latex`\unicode`)
|
注意:这一转义序列限制只对带标签的模板字面量移除,而不包括不带标签的模板字面量:
1 2
| let bad = `bad escape sequence: \unicode`
|
参考
http://exploringjs.com/es2018-es2019/toc.html
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/template_strings