ECMAScript 2018(ES9)新特性简介

简介: ECMAScript 2018(ES9)新特性简介

目录



简介


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; // SyntaxError
const {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.prototype
true
> Object.getPrototypeOf(clone2) === Object.prototype
true
> Object.getPrototypeOf({}) === Object.prototype
true


如果想要同时拷贝对象的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));
~~
> 注意,我们使用的所有的拷贝都是浅拷贝。如果被拷贝的对象内部又有对象的话,拷贝的只是这个对象的引用
~~~js
const original = { prop: {} };
const clone = Object.assign({}, original);
console.log(original.prop === clone.prop); // true
original.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 you
today?`);
// 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}' ] }
相关文章
|
7月前
|
存储 JavaScript 前端开发
ECMAScript 2020(ES11)新特性简介
ECMAScript 2020(ES11)新特性简介
63 0
|
7月前
|
JavaScript 前端开发 索引
ECMA 2022 (es13) 新特性
ECMA 2022 (es13) 新特性
36 0
|
8月前
|
Java
es6中简单常用的新特性
es6中简单常用的新特性
36 0
|
9月前
|
前端开发 JavaScript
es6的8条新特性总结
es6的8条新特性总结
109 0
|
JavaScript 前端开发 索引
ECMAScript 2022(ES13)初体验
2022 年 6 月 22 日,第 123 届 ECMA 大会批准了 ECMAScript 2022 语言规范,这意味着它现在正式成为标准。下面就来看看 ECMAScript 2022 有哪些新特性!
ECMAScript 2022(ES13)初体验
es6的新特性使用
使用 命令有: export、import 、export default
87 0
|
前端开发 JavaScript Java
ECMAScript 2021(ES12)新特性简介
ECMAScript 2021(ES12)新特性简介
|
存储 XML JSON
ECMAScript 2019(ES10)新特性简介
ECMAScript 2019(ES10)新特性简介
|
存储 前端开发 JavaScript
ECMAScript 2017(ES8)新特性简介
ECMAScript 2017(ES8)新特性简介
|
JavaScript 算法 API
ECMAScript 2016(ES7)新特性简介
ECMAScript 2016(ES7)新特性简介
ECMAScript 2016(ES7)新特性简介