写点什么

ECMAScript 2018(ES9) 新特性简介

发布于: 2021 年 03 月 31 日

简介

ES9 是 ECMA 协会在 2018 年 6 月发行的一个版本,因为是 ECMAScript 的第九个版本,所以也称为 ES9.


今天我们讲解一下 ES9 的新特性。


ES9 引入了 3 大特性和 2 个小的特性,我们接下来一一讲解。


异步遍历

在 ES6 中,引入了同步 iteration 的概念,随着 ES8 中的 Async 操作符的引用,在 ES9 中引入了异步遍历的新特性 Async iteration。


具体的内容可以参考我之前的文章 ES9的新特性:异步遍历Async iteration


Rest/Spread 操作符和对象构建

Rest 和 Spread 的操作符都是 … , 只不过使用的场景和目的不一样。


rest 主要用在对象的解构,目前只支持对象的解构和不确定的参数描述。


Spread 主要用在字面量对象的构建上。


下面我们分别来介绍:


Rest

如果用在对象的解构中,除了已经手动指定的属性名之外,rest 将会拷贝对象其他的所有可枚举(enumerable)的属性。


const obj = {foo: 1, bar: 2, baz: 3};const {foo, ...rest} = obj;    // Same as:    // const foo = 1;    // const rest = {bar: 2, baz: 3};
复制代码

如果用在参数中,rest 表示的是所有剩下的参数:


function func({param1, param2, ...rest}) { // rest operator    console.log('All parameters: ',        {param1, param2, ...rest}); // spread operator    return param1 + param2;}
复制代码

注意,在 Obj 字面量中,rest 运算符只能放在 obj 的最顶层,并且只能使用一次,还要放在最后。


const {...rest, foo} = obj; // SyntaxErrorconst {foo, ...rest1, ...rest2} = obj; // SyntaxError
复制代码

当然你还可以嵌套使用 rest 运算符:


const obj = {    foo: {        a: 1,        b: 2,        c: 3,    },    bar: 4,    baz: 5,};const {foo: {a, ...rest1}, ...rest2} = obj;// Same as:// const a = 1;// const rest1 = {b: 2, c: 3};// const rest2 = {bar: 4, baz: 5};
复制代码

Spread

spread 主要被用来展开对象,能够被展开对象的属性一定要是可枚举的 enumerable。


> const obj = {foo: 1, bar: 2};> {...obj, baz: 3}{ foo: 1, bar: 2, baz: 3 }
复制代码

如果对象的属性 key 一样,那么后面属性值会覆盖之前的属性值:


> const obj = {foo: 1, bar: 2, baz: 3};> {...obj, foo: true}{ foo: true, bar: 2, baz: 3 }> {foo: true, ...obj}{ foo: 1, bar: 2, baz: 3 }
复制代码

创建和拷贝对象

使用 Object.assign 和 Spread 操作符可以很方便的进行对象的拷贝。


我们看一个最简单的对象拷贝的例子:


const clone1 = {...obj};const clone2 = Object.assign({}, obj);
复制代码

但是这样的拷贝有个缺点,就是只能拷贝自有的可枚举的属性。


并且拷贝之后对象的 prototypes 是 Object.prototype,也就是说没有继承被拷贝对象的 prototype。


> Object.getPrototypeOf(clone1) === Object.prototypetrue> Object.getPrototypeOf(clone2) === Object.prototypetrue> Object.getPrototypeOf({}) === Object.prototypetrue
复制代码

如果想要同时拷贝对象的 prototype,则可以这样做:


const clone1 = {__proto__: Object.getPrototypeOf(obj), ...obj};const clone2 = Object.assign(    Object.create(Object.getPrototypeOf(obj)), obj);
复制代码

或者指定对象内置的 proto 属性,或者从 obj 的 prtotype 创建一个新的对象。


注意,对象内置的 proto 属性只在部分浏览器中支持。


Object.assign 和 spread 只能拷贝可枚举的属性,如果是 set,get 属性或者想要拷贝属性的 attributes(writable, enumerable),那么就需要用到我们之前讲到的 Object.getOwnPropertyDescriptors。


const clone1 = Object.defineProperties({},    Object.getOwnPropertyDescriptors(obj));
const clone2 = Object.create( Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));~~
> 注意,我们使用的所有的拷贝都是浅拷贝。如果被拷贝的对象内部又有对象的话,拷贝的只是这个对象的引用
~~~jsconst original = { prop: {} };const clone = Object.assign({}, original);
console.log(original.prop === clone.prop); // trueoriginal.prop.foo = 'abc';console.log(clone.prop.foo); // abc
复制代码

Spread 和 bject.assign() 的区别

assgin 在拷贝对象的时候,会调用相应属性的 set 方法,而 spread 不会。


举个例子,我们先给 Object.prototype 定义一个 set 方法:


Object.defineProperty(Object.prototype, 'foo', {    set(value) {        console.log('SET', value);    },});const obj = {foo: 123};
复制代码

然后看一下拷贝的区别:


> Object.assign({}, obj)SET 123{}
> { ...obj }{ foo: 123 }
复制代码

可以看到 assign 会触发 set 方法,而 spread 不会。


另外,如果对象属性是不可写的,那么 assign 将会报错,而 spread 不会。


我们先定义一个不可写的对象:


Object.defineProperty(Object.prototype, 'bar', {    writable: false,    value: 'abc',});
复制代码

看下赋值操作:


> const tmp = {};> tmp.bar = 123;TypeError: Cannot assign to read only property 'bar'
> Object.assign({}, obj)TypeError: Cannot assign to read only property 'bar'
> { ...obj }{ bar: 123 }
复制代码

正则表达式

ES9 的正则表达式新特性可以参考我的文章 ES9的新特性:正则表达式RegExp


promise.finally

promise 除了 then 和 catch 方法之外,还引入了新的 finally 方法。


和 java 中的 finally 一样,promise.finally 一定会被执行。


promise.then(result => {···}).catch(error => {···}).finally(() => {···});
复制代码

和 java 一样,我们可以在 finally 中做一些资源清理的工作:


let connection;db.open().then(conn => {    connection = conn;    return connection.select({ name: 'Jane' });}).then(result => {    ...})···.catch(error => {    // handle errors}).finally(() => {    connection.close();});
复制代码

上面的例子中,我们开启了一个数据库的连接,在使用完之后,我们在 finally 中对其进行 close 操作。


模板文字和带标签的模板文字

模板文字和带标签的模板文字是在 ES6 中引入的,在 ES9 中进行了修正。


我们先看下什么是模本文字,模板文字(Template literals)就是在反引号中输入的文字,在其中可以使用 ${···})来进行变量的解析,并且还支持回车换行。


const firstName = 'Jane';console.log(`Hello ${firstName}!How are youtoday?`);
// Output:// Hello Jane!// How are you// today?
复制代码

而带标签的模板文字是指在模板文字之前放上一个函数调用:


String.raw`\u{4B}`'\u{4B}'
复制代码

这里 String.raw 被称为 tag function,我们看下 raw 的定义:


raw(template: TemplateStringsArray, ...substitutions: any[]): string;
复制代码

上面的代码还可以改写为:


String.raw`\u004B`'\u004B'
复制代码

\u{4B}和 \u004B 都是字符 K 的 unicode 表示。


上面的 raw 其实可以这样表示:


function tagFunc(tmplObj, substs) {    return {        Cooked: tmplObj,        Raw: tmplObj.raw,    };}
复制代码

我们可以这样使用:


> tagFunc`\u{4B}`;{ Cooked: [ 'K' ], Raw: [ '\\u{4B}' ] }
复制代码

本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/ecmascript-9/

本文来源:flydean 的博客

欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!


发布于: 2021 年 03 月 31 日阅读数: 8
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
ECMAScript 2018(ES9)新特性简介