es7,es8,es9新特性

es7,es8,es9新特性


1. ES7新特性(ECMAScript 2016)

ES7在ES6的基礎上主要添加了兩項內容:javascript

  • Array.prototype.includes()方法
  • 求冪運算符(**)

Array.prototype.includes()方法

includes() 方法用來判斷一個數組是否包含一個指定的值,根據狀況,若是包含則返回 true,不然返回false。前端

// ex
var array = [1, 2, 3];

console.log(array.includes(2));
// expected output: true

var pets = ['cat', 'dog', 'bat'];

console.log(pets.includes('cat'));
// expected output: true

console.log(pets.includes('at'));
// expected output: false
複製代碼

Array.prototype.includes()方法接收兩個參數:java

  • 要搜索的值正則表達式

  • 搜索的開始索引typescript

當第二個參數被傳入時,該方法會從索引處開始日後搜索(默認索引值爲0)。若搜索值在數組中存在則返回true,不然返回false。 且看下面示例:數組

// ex:
['a', 'b', 'c', 'd'].includes('b')         // true
['a', 'b', 'c', 'd'].includes('b', 1)      // true
['a', 'b', 'c', 'd'].includes('b', 2)      // false
複製代碼

和indexOf的區別,請看代碼promise

// ex1:
var ary = [1];
if (ary.indexOf(1) !== -1) {
    console.log("數組存在1")
}
if (ary.includes(1)) {
    console.log("數組存在1")
}

//ex2:
var ary1 = [NaN];
console.log(ary1.indexOf(NaN))//-1
console.log(ary1.includes(NaN))//true

//ex3:
var ary1 = new Array(3);
console.log(ary1.indexOf(undefined));//-1
console.log(ary1.includes(undefined))//true

複製代碼

求冪運算符(**)

加/減法咱們一般都是用其中綴形式,直觀易懂。在ECMAScript2016中,咱們可使用**來替代Math.pow。bash

4 ** 3           // 64
// 等價於
Math.pow(4,3)

// 值得一提的是,做爲中綴運算符,**還支持如下操做
let n = 4;
n **= 3;
// 64
複製代碼

2.ES8新特性(ECMAScript 2017)

主要新功能:微信

  • 異步函數 Async Functions

次要新功能:異步

  • Object.values / Object.entries
  • String padding
  • Object.getOwnPropertyDescriptors()
  • 函數參數列表和調用中的尾逗號

Async Functions

Async Functions也就是咱們常說的Async/Await,相信你們對於這個概念都已經不陌生了。Async/Await是一種用於處理JS異步操做的語法糖,能夠幫助咱們擺脫回調地獄,編寫更加優雅的代碼。

通俗的理解,async關鍵字的做用是告訴編譯器對於標定的函數要區別對待。當編譯器遇到標定的函數中的await關鍵字時,要暫時中止運行,帶到await標定的函數處理完畢後,再進行相應操做。若是該函數fulfiled了,則返回值是fulfillment value,不然獲得的就是reject value。

幾種常見的用法,下面經過拿普通的promise寫法來對比,就很好理解了:

// ex:
async function asyncFunc() {
    const result = await otherAsyncFunc();
    console.log(result);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .then(result => {
        console.log(result);
    });
}

//按順序處理多個異步函數的時候優點更爲明顯:
async function asyncFunc() {
    const result1 = await otherAsyncFunc1();
    console.log(result1);
    const result2 = await otherAsyncFunc2();
    console.log(result2);
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc1()
    .then(result1 => {
        console.log(result1);
        return otherAsyncFunc2();
    })
    .then(result2 => {
        console.log(result2);
    });
}
// 並行處理多個異步函數:
async function asyncFunc() {
    const [result1, result2] = await Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ]);
    console.log(result1, result2);
}

// Equivalent to:
function asyncFunc() {
    return Promise.all([
        otherAsyncFunc1(),
        otherAsyncFunc2(),
    ])
    .then([result1, result2] => {
        console.log(result1, result2);
    });
}

// 處理錯誤:
async function asyncFunc() {
    try {
        await otherAsyncFunc();
    } catch (err) {
        console.error(err);
    }
}

// Equivalent to:
function asyncFunc() {
    return otherAsyncFunc()
    .catch(err => {
        console.error(err);
    });
}
複製代碼

Object.values and Object.entries

Object.values() 方法返回一個給定對象本身的全部可枚舉屬性值的數組,值的順序與使用for...in循環的順序相同 ( 區別在於for-in循環枚舉原型鏈中的屬性 )。

obj參數是須要待操做的對象。能夠是一個對象,或者一個數組(是一個帶有數字下標的對象,[10,20,30] -> {0: 10,1: 20,2: 30})。

// ex:
const obj = { x: 'xxx', y: 1 };
Object.values(obj); // ['xxx', 1]

const obj = ['e', 's', '8']; // 至關於 { 0: 'e', 1: 's', 2: '8' };
Object.values(obj); // ['e', 's', '8']

// 當咱們使用數字鍵值時,返回的是數字排序
// 根據鍵值排序
const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.values(obj); // ['yyy', 'zzz', 'xxx']

Object.values('es8'); // ['e', 's', '8']
複製代碼

Object.entries 方法返回一個給定對象自身可遍歷屬性 [key, value] 的數組, 排序規則和 Object.values 同樣。這個方法的聲明比較瑣碎:

// ex:
const obj = { x: 'xxx', y: 1 };
Object.entries(obj); // [['x', 'xxx'], ['y', 1]]

const obj = ['e', 's', '8'];
Object.entries(obj); // [['0', 'e'], ['1', 's'], ['2', '8']]

const obj = { 10: 'xxx', 1: 'yyy', 3: 'zzz' };
Object.entries(obj); // [['1', 'yyy'], ['3', 'zzz'], ['10': 'xxx']]

Object.entries('es8'); // [['0', 'e'], ['1', 's'], ['2', '8']]
複製代碼

String padding

爲 String 對象增長了 2 個函數:padStart 和 padEnd。

像它們名字那樣,這幾個函數的主要目的就是填補字符串的首部和尾部,爲了使獲得的結果字符串的長度能達到給定的長度。你能夠經過特定的字符,或者字符串,或者默認的空格填充它。下面是函數的聲明:

str.padStart(targetLength [, padString])
str.padEnd(targetLength [, padString])
複製代碼

這些函數的第一個參數是 targetLength(目標長度),這個是結果字符串的長度。第二個參數是可選的 padString(填充字符),一個用於填充到源字符串的字符串。默認值是空格。

'es8'.padStart(2);          // 'es8'
'es8'.padStart(5);          // ' es8'
'es8'.padStart(6, 'woof');  // 'wooes8'
'es8'.padStart(14, 'wow');  // 'wowwowwowwoes8'
'es8'.padStart(7, '0');     // '0000es8'

'es8'.padEnd(2);            // 'es8'
'es8'.padEnd(5);            // 'es8 '
'es8'.padEnd(6, 'woof');    // 'es8woo'
'es8'.padEnd(14, 'wow');    // 'es8wowwowwowwo'
'es8'.padEnd(7, '6');       // 'es86666'
複製代碼

Object.getOwnPropertyDescriptors

getOwnPropertyDescriptors 方法返回指定對象全部自身屬性的描述對象。屬性描述對象是直接在對象上定義的,而不是繼承於對象的原型。ES2017加入這個函數的主要動機在於方便將一個對象深度拷貝給另外一個對象,同時能夠將getter/setter拷貝。聲明以下:

Object.getOwnPropertyDescriptors(obj)
複製代碼

obj 是待操做對象。返回的描述對象鍵值有:configurable, enumerable, writable, get, set and value。

const obj = { 
  get es7() { return 777; },
  get es8() { return 888; }
};
Object.getOwnPropertyDescriptors(obj);
// {
// es7: {
// configurable: true,
// enumerable: true,
// get: function es7(){}, //the getter function
// set: undefined
// },
// es8: {
// configurable: true,
// enumerable: true,
// get: function es8(){}, //the getter function
// set: undefined
// }
// }

複製代碼

結尾逗號

結尾逗號用代碼展現很是明瞭:

// 參數定義時
function foo( param1, param2, ) {}

// 函數調用時
foo(
    'abc',
    'def',
);

// 對象中
let obj = {
    first: 'Jane',
    last: 'Doe',
};

// 數組中
let arr = [
    'red',
    'green',
    'blue',
];

複製代碼

這個改動有什麼好處呢?

  • 首先,從新排列項目更簡單,由於若是最後一項更改其位置,則沒必要添加和刪除逗號。
  • 其次,它能夠幫助版本控制系統跟蹤實際發生的變化。例如,從:
[
    'foo'
]

複製代碼

修改成

[
    'foo',
    'bar'
]

複製代碼

致使線條'foo'和線條'bar'被標記爲已更改,即便惟一真正的變化是後一條線被添加。

3,ES9新特性(ECMAScript 2018)

主要新功能:

  • 異步迭代
  • Rest/Spread 屬性

新的正則表達式功能:

  • RegExp named capture groups
  • RegExp Unicode Property Escapes(Mathias Bynens)
  • RegExp Lookbehind Assertions

其餘新功能:

  • Promise.prototype.finally()
  • 模板字符串修改

異步迭代

首先來回顧一下同步迭代器:

ES6引入了同步迭代器,其工做原理以下:

  • Iterable:一個對象,表示能夠經過Symbol.iterator方法進行迭代。
  • Iterator:經過調用iterable [Symbol.iterator] ()返回的對象。它將每一個迭代元素包裝在一個對象中,並經過其next()方法一次返回一個。
  • IteratorResult:返回的對象next()。屬性value包含一個迭代的元素,屬性done是true 後最後一個元素。

示例:

const iterable = ['a', 'b'];
const iterator = iterable[Symbol.iterator]();
iterator.next()
// { value: 'a', done: false }
iterator.next()
// { value: 'b', done: false }
iterator.next()
// { value: undefined, done: true }

複製代碼

異步迭代器

先前的迭代方式是同步的,並不適用於異步數據源。例如,在如下代碼中,readLinesFromFile()沒法經過同步迭代傳遞其異步數據:

function readLinesFromFile (){
        setTimeout(() => {
            return "123456"
        },1000)
}
for (const line of readLinesFromFile()) {
    console.log(line);
}
function *createNumberIterator(){
        yield 1;
        yield 2;
        yield 3;
    }
    const iterator = createNumberIterator();
    const p2 = iterator.next(); // Object {value: 2, done: false}
    const p3 = iterator.next(); // Object {value: 3, done: false}
    const p4 = iterator.next(); // Object {value: undefined, done: true}
    const p1 = iterator.next(); // Object {value: 1, done: false}
    log(p1, p2, p3, p4)
複製代碼

異步迭代器和常規迭代器的工做方式很是類似,可是異步迭代器涉及promise:

async function example() {
  // 普通迭代器:
  const iterator = createNumberIterator();
  iterator.next(); // Object {value: 1, done: false}
  iterator.next(); // Object {value: 2, done: false}
  iterator.next(); // Object {value: 3, done: false}
  iterator.next(); // Object {value: undefined, done: true}

  // 異步迭代器:
  const asyncIterator = createAsyncNumberIterator();
  const p = asyncIterator.next(); // Promise
  await p;// Object {value: 1, done: false}
  await asyncIterator.next(); // Object {value: 2, done: false}
  await asyncIterator.next(); // Object {value: 3, done: false}
  await asyncIterator.next(); // Object {value: undefined, done: true}
}
複製代碼

異步迭代器對象的next()方法返回了一個Promise,解析後的值跟普通的迭代器相似。 用法:iterator.next().then(({ value, done })=> {//{value: ‘some val’, done: false}}

const promises = [
    new Promise(resolve => resolve(1)),
    new Promise(resolve => resolve(2)),
    new Promise(resolve => resolve(3)),
];

async function test() {
    for await (const p of promises) {
        console.log(p);
    }
}
test(); //1 ,2 3
複製代碼

Rest/Spread 屬性

這個就是咱們一般所說的rest參數和擴展運算符,這項特性在ES6中已經引入,可是ES6中的做用對象僅限於數組

restParam(1, 2, 3, 4, 5);

function restParam(p1, p2, ...p3) {
  // p1 = 1
  // p2 = 2
  // p3 = [3, 4, 5]
}

const values = [99, 100, -1, 48, 16];
console.log( Math.max(...values) ); // 100
複製代碼

在ES9中,爲對象提供了像數組同樣的rest參數和擴展運算符:

const obj = {
  a: 1,
  b: 2,
  c: 3
}
const { a, ...param } = obj;
  console.log(a)     //1
  console.log(param) //{b: 2, c: 3}

function foo({a, ...param}) {
  console.log(a);    //1
  console.log(param) //{b: 2, c: 3}
}
複製代碼

正則表達式命名捕獲組

編號的捕獲組

//正則表達式命名捕獲組
const RE_DATE = /([0-9]{4})-([0-9]{2})-([0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj[1]; // 1999
const month = matchObj[2]; // 12
const day = matchObj[3]; // 31
複製代碼

經過數字引用捕獲組有幾個缺點:

  • 找到捕獲組的數量是一件麻煩事:必須使用括號。
  • 若是要了解組的用途,則須要查看正則表達式。
  • 若是更改捕獲組的順序,則還必須更改匹配代碼。

命名的捕獲組

ES9中能夠經過名稱來識別捕獲組:(?<year>[0-9]{4})

在這裏,咱們用名稱標記了前一個捕獲組year。該名稱必須是合法的JavaScript標識符(認爲變量名稱或屬性名稱)。匹配後,您能夠經過訪問捕獲的字符串matchObj.groups.year來訪問。

讓咱們重寫前面的代碼:

const RE_DATE = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;

const matchObj = RE_DATE.exec('1999-12-31');
const year = matchObj.groups.year; // 1999
const month = matchObj.groups.month; // 12
const day = matchObj.groups.day; // 31

// 使用解構語法更爲簡便
const {groups: {day, year}} = RE_DATE.exec('1999-12-31');
console.log(year); // 1999
console.log(day); // 31
複製代碼

能夠發現,命名捕獲組有如下優勢:

  • 找到捕獲組的「ID」更容易。
  • 匹配代碼變爲自描述性的,由於捕獲組的ID描述了正在捕獲的內容。
  • 若是更改捕獲組的順序,則無需更改匹配代碼。
  • 捕獲組的名稱也使正則表達式更容易理解,由於您能夠直接看到每一個組的用途。

正則表達式 Unicode 轉義

該特性容許您使用\p{}經過說起大括號內的Unicode字符屬性來匹配字符,在正則表達式中使用標記 u (unicode) 設置。

/^\p{White_Space}+$/u.test('\t \n\r')
// true
/^\p{Script=Greek}+$/u.test('μετά')
// true
複製代碼

新方法匹配中文字符

因爲在Unicode裏面,中文字符對應的Unicode Script是Han,因而咱們就能夠用這個reg來匹配中文:

/\p{Script=Han}/u 這樣咱們就能夠不用記憶繁瑣又很差記的/[\u4e00-\u9fa5]/了,何況這個表達式已經有些年頭了,說實話,後來又新增的屬性爲Han的字符並不在這個範圍內,所以這個有年頭reg並不必定好使。

我隨便從網上找了一個Unicode8.0添加的中文字符「錀」,我測了一下兩種reg的兼容性:

oldReg=/[\u4e00-\u9fa5]/
newReg=/\p{Script=Han}/u

oldReg.test('abc')
// false
newReg.test('abc')
// false

oldReg.test('地平線')
// true
newReg.test('地平線')
// true

oldReg.test('錀')
// false
newReg.test('錀')
// true
複製代碼

正則表達式反向斷言

先來看下正則表達式先行斷言是什麼:

如獲取貨幣的符號

const noReLookahead = /\D(\d+)/,
      reLookahead = /\D(?=\d+)/,
      match1 = noReLookahead.exec('$123.45'),
      match2 = reLookahead.exec('$123.45');
console.log(match1[0]); // $123 
console.log(match2[0]); // $
複製代碼

在ES9中能夠容許反向斷言:

const reLookahead = /(?<=\D)[\d\.]+/;
      match = reLookahead.exec('$123.45');
console.log(match[0]); // 123.45
複製代碼

使用?<=進行反向斷言,可使用反向斷言獲取貨幣的價格,而忽略貨幣符號。

Promise.prototype.finally()

promise
  .then(result => {···})
  .catch(error => {···})
  .finally(() => {···});
複製代碼

finally的回調總會被執行。注意.finally()執行以後便在.then 或者.catch的話是不會執行的

模板字符串修改

ES2018 移除對 ECMAScript 在帶標籤的模版字符串中轉義序列的語法限制。 以前,\u開始一個 unicode 轉義,\x開始一個十六進制轉義,\後跟一個數字開始一個八進制轉義。這使得建立特定的字符串變得不可能,例如Windows文件路徑 C:\uuu\xxx\111。

要取消轉義序列的語法限制,可在模板字符串以前使用標記函數String.raw:

`\u{54}`
// "T"
String.raw`\u{54}`
// "\u{54}"
複製代碼

更多前端乾貨內容在「天天學前端」微信公衆號內,歡迎關注

參考

相關文章
相關標籤/搜索