专栏首页全栈者重温基础:ES9系列

重温基础:ES9系列

ES9系列目录

  • 1 对象的拓展运算符
  • 2 正则表达式 s 修饰符
  • 3 异步遍历器

所有整理的文章都收录到我《Cute-JavaScript》系列文章中,访问地址:http://js.pingan8787.com

1 对象的拓展运算符

1.1 介绍

对象的拓展运算符,即对象的Rest/Spread属性,可将对象解构赋值用于从一个对象取值,搜键值对分配到指定对象上,与数组的拓展运算符类似:

let  {x, y, ...z} = {x:1, y:2, a:3, b:4};
x;  // 1
y;  // 2
z;  // {a:3, b:4}

对象的解构赋值要求等号右边必须是个对象,所以如果等号右边是 undefinednull就会报错无法转成对象。

let {a, ...b} = null;      // 运行时报错
let {a, ...b} = undefined; // 运行时报错

解构赋值必须是最后一个参数,否则报错。

let {...a, b, c} = obj;     // 语法错误
let {a, ...b, c} = obj;     // 语法错误

注意

  • 1.解构赋值是浅拷贝。
let a = {a1: {a2: 'leo'}};
let {...b} = a;
a.a1.a2 = 'leo';
b.a1.a2 = 'leo';
  • 2.拓展运算符的解构赋值,不能复制继承自原型对象的属性。
let o1 = { a: 1 };
let o2 = { b: 2 };
o2.__proto__ = o1;
let { ...o3 } = o2;
o3;    // { b: 2 }
o3.a;  // undefined

1.2 使用场景

  • 1.取出参数对象所有可遍历属性,拷贝到当前对象中。
let a = { a1:1, a2:2 };
let b = { ...a };
b;   // { a1:1, a2:2 }

// 类似Object.assign方法
  • 2.合并两个对象。
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);
  • 3.将自定义属性放在拓展运算符后面,覆盖对象原有的同名属性。
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}
  • 4.将自定义属性放在拓展运算符前面,就会成为设置新对象的默认值。
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}
  • 5.拓展运算符后面可以使用表达式。
let a = {
    ...(x>1? {a:!:{}),
    b:2
}
  • 6.拓展运算符后面如果是个空对象,则没有任何效果。
{...{}, a:1};  // {a:1}
  • 7.若参数是 nullundefined则忽略且不报错。
let a = { ...null, ...undefined }; // 不报错
  • 8.若有取值函数 get则会执行。
// 不会打印 因为f属性只是定义 而不没执行
let a = {
    ...a1,
    get f(){console.log(1)}
}

// 会打印 因为f执行了
let a = {
    ...a1,
    ...{
        get f(){console.log(1)}
    }
}

2 正则表达式 s 修饰符

在正则表达式中,点( .)可以表示任意单个字符,除了两个:用 u修饰符解决四个字节的UTF-16字符,另一个是行终止符。 终止符即表示一行的结束,如下四个字符属于“行终止符”:

  • U+000A 换行符(\n)
  • U+000D 回车符(\r)
  • U+2028 行分隔符(line separator)
  • U+2029 段分隔符(paragraph separator)
/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不冲突,两者一起使用的情况下, .匹配所有字符,而 ^$匹配每一行的行首和行尾。

3 异步遍历器

在前面ES6章节中,介绍了Iterator接口,而ES6引入了“异步遍历器”,是为异步操作提供原生的遍历器接口,即 valuedone这两个属性都是异步产生的。

3.1 异步遍历的接口

通过调用遍历器的 next方法,返回一个Promise对象。

a.next().then( 
    ({value, done}) => {
        //...
    }
)

上述 a为异步遍历器,调用 next后返回一个Promise对象,再调用 then方法就可以指定Promise对象状态变为 resolve后执行的回调函数,参数为 valuedone两个属性的对象,与同步遍历器一致。 与同步遍历器一样,异步遍历器接口也是部署在 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();

3.2 for await...of

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对象被 rejectforawait...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

3.3 异步Generator函数

就像 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函数中,同时使用 awaityield,简单样理解, 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;
  }
}

3.4 yield* 语句

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

本文分享自微信公众号 - 全栈者(fullStackEngineer)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • MicroPython TPYBoard v201 简易家庭气象站的实现过程

    上一篇教程中我们实现了一个简单网页的显示。本篇呢,增加上温湿、时间等信息的展示,实现一个简单的家庭气象站。

    阿莉埃蒂
  • 7个Python实战项目代码,让你分分钟晋级大神!

    1、你不知道已经有哪些轮子已经造好了,哪个适合你用。有名有姓的的著名轮子就400多个,更别说没名没姓自己在制造中的轮子。

    用户4962466
  • python爬虫学习,这里有一条高效的学习路径

    对于小白来说,爬虫可能是一件非常复杂、技术门槛很高的事情。比如有的人认为学爬虫必须精通 Python,然后哼哧哼哧系统学习 Python 的每个知识点,很久之后...

    python学习教程
  • python爬虫学习,python抓取百度音乐mp3歌曲

    python抓取百度音乐mp3歌曲,目前成功率不是100%,因为我每首歌只抓一遍,没有去判断抓取成功情况和链接速度,还有我取得歌曲名称的方式也有点不合适,对歌曲...

    python学习教程
  • PTF:一款多模块渗透测试框架

    The PenTesters Framework (PTF)是一个针对Debian/Ubuntu/ArchLinux开发设计的Python脚本,在PTF的帮助下...

    FB客服
  • 20 Python 基础: 重点知识点--网络通信进阶知识讲解

    其他文章均已发表,可通过 “技术专栏 -- Python -- PY基础” 进行阅读。

    小Gy
  • 爬虫教程入门(第一章)

    网络爬虫,也叫网络蜘蛛(Web Spider),如果把互联网比喻成一个蜘蛛网,Spider就是一只在网上爬来爬去的蜘蛛。网络爬虫就是根据网页的地址来寻找网页的,...

    天钧
  • 模块(十九章)

    模块是一个包含所有你定义的函数和变量的文件,其后缀名是py模块可以被背的程序引用,以使用该模块中的函数等功能,这就是使用Python标准库的方法

    天钧
  • 反运算(简单的定制)[第十七章]

    关于反运算,这里要注意一点;对于a + b,b的__radd__(self,other),中other是a的对象,self才是b的对象

    天钧
  • LeetCode 138:复制带随机指针的链表 Copy List with Random Pointer

    给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

    爱写bug

扫码关注云+社区

领取腾讯云代金券