ECMAScript 2018(ES9)新特性簡介

簡介

ES9是ECMA協會在2018年6月發行的一個版本,由於是ECMAScript的第九個版本,因此也稱爲ES9.java

今天咱們講解一下ES9的新特性。正則表達式

ES9引入了3大特性和2個小的特性,咱們接下來一一講解。數據庫

異步遍歷

在ES6中,引入了同步iteration的概念,隨着ES8中的Async操做符的引用,在ES9中引入了異步遍歷的新特性Async iteration。promise

具體的內容能夠參考我以前的文章 [ES9的新特性:異步遍歷Async iteration]()瀏覽器

Rest/Spread操做符和對象構建

Rest和Spread的操做符都是 ... , 只不過使用的場景和目的不同。ecmascript

rest主要用在對象的解構,目前只支持對象的解構和不肯定的參數描述。異步

Spread主要用在字面量對象的構建上。函數

下面咱們分別來介紹:prototype

Rest

若是用在對象的解構中,除了已經手動指定的屬性名以外,rest將會拷貝對象其餘的全部可枚舉(enumerable)的屬性。rest

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));
~~

> 注意,咱們使用的全部的拷貝都是淺拷貝。若是被拷貝的對象內部又有對象的話,拷貝的只是這個對象的引用

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}' ] }
> 本文做者:flydean程序那些事
> 
> 本文連接:[http://www.flydean.com/ecmascript-9/](http://www.flydean.com/ecmascript-9/)
> 
> 本文來源:flydean的博客
> 
> 歡迎關注個人公衆號:「程序那些事」最通俗的解讀,最深入的乾貨,最簡潔的教程,衆多你不知道的小技巧等你來發現!
相關文章
相關標籤/搜索