ES9系列目录
所有整理的文章都收录到我《Cute-JavaScript》系列文章中,访问地址:http://js.pingan8787.com
对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似:
let {x, y, ...z} = {x:1, y:2, a:3, b:4};
x; // 1
y; // 2
z; // {a:3, b:4}
对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是 undefined
或 null
就会报错无法转成对象。
let {a, ...b} = null; // 运行时报错
let {a, ...b} = undefined; // 运行时报错
解构赋值必须是最后一个参数,否则报错。
let {...a, b, c} = obj; // 语法错误
let {a, ...b, c} = obj; // 语法错误
注意:
let a = {a1: {a2: 'leo'}};
let {...b} = a;
a.a1.a2 = 'leo';
b.a1.a2 = 'leo';
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3; // { b: 2 }
o3.a; // undefined
let a = { a1:1, a2:2 };
let b = { ...a };
b; // { a1:1, a2:2 }
// 类似Object.assign方法
let a = { a1:1, a2:2 };
let b = { b1:11, b2:22 };
let ab = { ...a, ...b }; // {a1: 1, a2: 2, b1: 11, b2: 22}
// 等同于
let ab = Object.assign({}, a, b);
let a = { a1:1, a2:2, a3:3 };
let r = { ...a, a3:666 };
// r {a1: 1, a2: 2, a3: 666}
// 等同于
let r = { ...a, ...{ a3:666 }};
// r {a1: 1, a2: 2, a3: 666}
// 等同于
let r = Object.assign({}, a, { a3:666 });
// r {a1: 1, a2: 2, a3: 666}
let a = { a1:1, a2:2 };
let r = { a3:666, ...a };
// r {a3: 666, a1: 1, a2: 2}
// 等同于
let r = Object.assign({}, {a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}
// 等同于
let r = Object.assign({a3:666}, a);
// r {a3: 666, a1: 1, a2: 2}
let a = {
...(x>1? {a:!:{}),
b:2
}
{...{}, a:1}; // {a:1}
null
或 undefined
则忽略且不报错。let a = { ...null, ...undefined }; // 不报错
get
则会执行。// 不会打印 因为f属性只是定义 而不没执行
let a = {
...a1,
get f(){console.log(1)}
}
// 会打印 因为f执行了
let a = {
...a1,
...{
get f(){console.log(1)}
}
}
在正则表达式中,点( .
)可以表示任意单个字符,除了两个:用 u
修饰符解决四个字节的UTF-16字符,另一个是行终止符。
终止符即表示一行的结束,如下四个字符属于“行终止符”:
/foo.bar/.test('foo\nbar')
// false
上面代码中,因为 .
不匹配 \n
,所以正则表达式返回 false
。
换个醒,可以匹配任意单个字符:
/foo[^]bar/.test('foo\nbar')
// true
ES9引入 s
修饰符,使得 .
可以匹配任意单个字符:
/foo.bar/s.test('foo\nbar') // true
这被称为 dotAll
模式,即点( dot
)代表一切字符。所以,正则表达式还引入了一个 dotAll
属性,返回一个布尔值,表示该正则表达式是否处在 dotAll
模式。
const re = /foo.bar/s;
// 另一种写法
// const re = new RegExp('foo.bar', 's');
re.test('foo\nbar') // true
re.dotAll // true
re.flags // 's'
/s
修饰符和多行修饰符 /m
不冲突,两者一起使用的情况下, .
匹配所有字符,而 ^
和 $
匹配每一行的行首和行尾。
在前面ES6章节中,介绍了Iterator接口,而ES6引入了“异步遍历器”,是为异步操作提供原生的遍历器接口,即 value
和 done
这两个属性都是异步产生的。
通过调用遍历器的 next
方法,返回一个Promise对象。
a.next().then(
({value, done}) => {
//...
}
)
上述 a
为异步遍历器,调用 next
后返回一个Promise对象,再调用 then
方法就可以指定Promise对象状态变为 resolve
后执行的回调函数,参数为 value
和 done
两个属性的对象,与同步遍历器一致。
与同步遍历器一样,异步遍历器接口也是部署在 Symbol.asyncIterator
属性上,只要有这个属性,就都可以异步遍历。
let a = createAsyncIterable(['a', 'b']);
//createAsyncIterable方法用于构建一个iterator接口
let b = a[Symbol.asyncInterator]();
b.next().then( result1 => {
console.log(result1); // {value: 'a', done:false}
return b.next();
}).then( result2 => {
console.log(result2); // {value: 'b', done:false}
return b.next();
}).then( result3 => {
console.log(result3); // {value: undefined, done:true}
})
另外 next
方法返回的是一个Promise对象,所以可以放在 await
命令后。
async function f(){
let a = createAsyncIterable(['a', 'b']);
let b = a[Symbol.asyncInterator]();
console.log(await b.next());// {value: 'a', done:false}
console.log(await b.next());// {value: 'b', done:false}
console.log(await b.next());// {value: undefined, done:true}
}
还有一种情况,使用 Promise.all
方法,将所有的 next
按顺序连续调用:
let a = createAsyncIterable(['a', 'b']);
let b = a[Symbol.asyncInterator]();
let {{value:v1}, {value:v2}} = await Promise.all([
b.next(), b.next()
])
也可以一次调用所有 next
方法,再用 await
最后一步操作。
async function f(){
let write = openFile('aaa.txt');
write.next('hi');
write.next('leo');
await write.return();
}
f();
for...of
用于遍历同步的Iterator接口,而ES8引入 forawait...of
遍历异步的Iterator接口。
async function f(){
for await(let a of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
上面代码, createAsyncIterable()
返回一个拥有异步遍历器接口的对象, for...of
自动调用这个对象的 next
方法,得到一个Promise对象, await
用来处理这个Promise,一但 resolve
就把得到的值 x
传到 for...of
里面。
用途 直接把部署了asyncIteable操作的异步接口放入这个循环。
let a = '';
async function f(){
for await (let b of req) {
a += b;
}
let c = JSON.parse(a);
console.log('leo', c);
}
当 next
返回的Promise对象被 reject
, forawait...of
就会保错,用 try...catch
捕获。
async function f(){
try{
for await (let a of iterableObj()){
console.log(a);
}
}catch(e){
console.error(e);
}
}
注意, forawait...of
循环也可以用于同步遍历器。
(async function () {
for await (let a of ['a', 'b']) {
console.log(a);
}
})();
// a
// b
就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。
在语法上,异步 Generator 函数就是 async
函数与 Generator 函数的结合。
async function* f() {
yield 'hi';
}
const a = f();
a.next().then(x => console.log(x));
// { value: 'hello', done: false }
设计异步遍历器的目的之一,就是为了让Generator函数能用同一套接口处理同步和异步操作。
// 同步Generator函数
function * f(iterable, fun){
let a = iterabl[Symbol.iterator]();
while(true){
let {val, done} = a.next();
if(done) break;
yield fun(val);
}
}
// 异步Generator函数
async function * f(iterable, fun){
let a = iterabl[Symbol.iterator]();
while(true){
let {val, done} = await a.next();
if(done) break;
yield fun(val);
}
}
同步和异步Generator函数相同点:在 yield
时用 next
方法停下,将后面表达式的值作为 next()
返回对象的 value
。
在异步Generator函数中,同时使用 await
和 yield
,简单样理解, await
命令用于将外部操作产生的值输入函数内部, yield
命令用于将函数内部的值输出。
(async function () {
for await (const line of readLines(filePath)) {
console.log(line);
}
})()
异步 Generator 函数可以与 forawait...of
循环结合起来使用。
async function* f(asyncIterable) {
for await (const line of asyncIterable) {
yield '> ' + line;
}
}
yield*
语句跟一个异步遍历器。
async function * f(){
yield 'a';
yield 'b';
return 'leo';
}
async function * g(){
const a = yield* f(); // a => 'leo'
}
与同步 Generator 函数一样, forawait...of
循环会展开 yield*
。
(async function () {
for await (const x of gen2()) {
console.log(x);
}
})();
// a
// b