1. 程式人生 > 實用技巧 >es6/es7/es8常用新特性總結(超實用)

es6/es7/es8常用新特性總結(超實用)

 本文標題有誤導性,因為我其實想寫node8的新特性,說實話一下子從node v1.x跳躍到node 8.x+ 真有點受寵若驚的感覺。一直覺得node 陣列、 物件、序列等的處理沒有python方便,因為需要藉助外部的一些包比如underscore /lodash才能實現,但是接下來是見證奇蹟的時刻,es6/7/8讓node程式碼變的更簡潔、更易懂。

  Node.js的優點之一就是前後端用同一種語言,本質上還是js,因此可以通過babel來使nodejs支援對應的ECMAScript。

目錄

  • 如何讓Nodejs支援對應的ES
  • ES6常用新特性
    • let && const
    • iterable型別
    • 解構賦值
    • =>函式
    • ...操作符
  • ES7新特性
    • Array.prototype.includes
    • Exponentiation Operator(求冪運算)
  • ES8新特性
    • Object.values/Object.entries
    • String padding(字串填充)
    • Object.getOwnPropertyDescriptors
    • 函式引數列表和呼叫中的尾逗號(Trailing commas)
    • 非同步函式(Async Functions)

如何讓Nodejs支援對應的ES

  不同版本的Node.js對Babel有不同的支援,如若是Nodejs支援ES6語法,需要引入babel。因此要安裝一些babel的依賴包,如babel-preset-es2015 / babel-core /babel-cli。

ES6對應es2015,ES7對應es2016,ES8對應es2017,同時對應支援的node版本更高。

檢測ES6

可以使用es-checker來檢測當前Node.js對ES6的支援情況,全域性安裝es-checker

$ npm install -g es-checker

安裝好之後,執行以下命令來檢視Node.js對ES6的支援情況:

$ es-checker

可以從輸出中檢視當前版本Node.js(v7.7.4)對ES6的支援情況:

$ es-checker

ECMAScript 6 Feature Detection (v1.4.1)

Variables
  √ let and const
  √ TDZ error for too-early access of let or const declarations
  √ Redefinition of const declarations not allowed
  √ destructuring assignments/declarations for arrays and objects
  √ ... operator

Data Types
  √ For...of loop
  √ Map, Set, WeakMap, WeakSet
  √ Symbol
  √ Symbols cannot be implicitly coerced

Number
  √ Octal (e.g. 0o1 ) and binary (e.g. 0b10 ) literal forms
  √ Old octal literal invalid now (e.g. 01 )
  √ Static functions added to Math (e.g. Math.hypot(), Math.acosh(), Math.imul() )
  √ Static functions added to Number (Number.isNaN(), Number.isInteger() )

String
  √ Methods added to String.prototype (String.prototype.includes(), String.prototype.repeat() )
  √ Unicode code-point escape form in string literals (e.g. \u{20BB7} )
  √ Unicode code-point escape form in identifier names (e.g. var \u{20BB7} = 42; )
  √ Unicode code-point escape form in regular expressions (e.g. var regexp = /\u{20BB7}/u; )
  √ y flag for sticky regular expressions (e.g. /b/y )
  √ Template String Literals

Function
  √ arrow function
  √ default function parameter values
  √ destructuring for function parameters
  √ Inferences for function name property for anonymous functions
  × Tail-call optimization for function calls and recursion

Array
  × Methods added to Array.prototype ([].fill(), [].find(), [].findIndex(), [].entries(), [].keys(), [].values() )
  √ Static functions added to Array (Array.from(), Array.of() )
  √ TypedArrays like Uint8Array, ArrayBuffer, Int8Array(), Int32Array(), Float64Array()
  √ Some Array methods (e.g. Int8Array.prototype.slice(), Int8Array.prototype.join(), Int8Array.prototype.forEach() ) added to the TypedArray prototypes
  √ Some Array statics (e.g. Uint32Array.from(), Uint32Array.of() ) added to the TypedArray constructors

Object
  √ __proto__ in object literal definition sets [[Prototype]] link
  √ Static functions added to Object (Object.getOwnPropertySymbols(), Object.assign() )
  √ Object Literal Computed Property
  √ Object Literal Property Shorthands
  √ Proxies
  √ Reflect

Generator and Promise
  √ Generator function
  √ Promises

Class
  √ Class
  √ super allowed in object methods
  √ class ABC extends Array { .. }

Module
  × Module export command
  × Module import command


=========================================
Passes 38 feature Detections
Your runtime supports 90% of ECMAScript 6
=========================================

新增es6支援

全域性安裝babel-cli, 專案安裝 babel-preset-es2015:

npm install babel-cli -g
npm install babel-preset-es2015 --save

安裝完之後,還需要新增一個名為.babelrc的配置檔案。方便babel-cli使用babel-preset-es2015。檔案內容如下:

{
  "presets": ["es2015"],
  "plugins": [
    "add-module-exports"
  ]
}

或者在專案入口檔案(如app.js)引用下babel:

require('babel-register')({
    presets: ['es2015']
});

如此,再也不會報如下的錯誤:

import a from './config';
^^^^^^
SyntaxError: Unexpected token import

並且也會支援ES6的新特性。上述是描述node如何支援ES6的,支援ES8是類似的,preset指定"presets": ["es2017"]即可。

下面進入正題,到底ES6~8有哪些實用的新特性呢

ES6常用新特性

let && const

let 命令也用於變數宣告,但是作用域為區域性

{
    let a = 10;
    var b = 1;        
}

在函式外部可以獲取到b,獲取不到a,因此例如for迴圈計數器就適合使用let。

const用於宣告一個常量,設定後值不會再改變

const PI = 3.1415;
PI // 3.1415
PI = 3; //TypeError: Assignment to constant variable.

iterable型別

為了統一集合型別,ES6標準引入了新的iterable型別,Array、Map和Set都屬於iterable型別,具有iterable型別的集合可以通過新的for … of迴圈來遍歷。

var a = ['A', 'B', 'C'];
var s = new Set(['A', 'B', 'C']);
var m = new Map([[1, 'x'], [2, 'y'], [3, 'z']]);
for (var x of a) { // 遍歷Array
    alert(x);
}
for (var x of s) { // 遍歷Set
    alert(x);
}
for (var x of m) { // 遍歷Map
    alert(x[0] + '=' + x[1]);
}

Map相關操作如下, Set同理:

var m = new Map(); // 空Map
m.set('Adam', 67); // 新增新的key-value
m.set('Bob', 59);
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 刪除key 'Adam'
m.get('Adam'); // undefined

解構賦值

ES6 允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructuring)。
例如陣列:

let [a, b, c] = [1, 2, 3];
//等同於
let a = 1;
let b = 2;
let c = 3;

這真的讓程式碼看起來更優美,有種python賦值的既視感。

物件的解構賦值:獲取物件的多個屬性並且使用一條語句將它們賦給多個變數。

var {
  StyleSheet,
  Text,
  View
} = React;

等同於
var StyleSheet = React.StyleSheet;
var Text = React.Text;
var View = React.Text;

箭頭函式

ES6中新增箭頭操作符用於簡化函式的寫法,操作符左邊為引數,右邊為具體操作和返回值。

var sum = (num1, num2) => { return num1 + num2; }
//等同於
var sum = function(num1, num2) {
    return num1 + num2;
};

箭頭函式還修復了this的指向,使其永遠指向詞法作用域:

var obj = {
    birth: 1990,
    getAge: function () {
        var b = this.birth; // 1990
        var fn = () => new Date().getFullYear() - this.birth; // this指向obj物件
        return fn();
    }
};
obj.getAge(); // 25

...操作符

這個的引入幾乎不會用到extend這個函式來。通過它可以將陣列作為引數直接傳入函式:

var people=['Wayou','John','Sherlock'];
function sayHello(people1,people2,people3){
    console.log(`Hello ${people1},${people2},${people3}`);
}
//改寫為
sayHello(...people);//輸出:Hello Wayou,John,Sherlock 

在函式定義時可以通過…rest獲取定義引數外的所有引數(類似C#中的引數陣列,可以有任意多個引數):

function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
}

foo(1, 2, 3, 4, 5);
// 結果:
// a = 1
// b = 2
// Array [ 3, 4, 5 ]

這個真是完美!關於更多...的說明參考這篇部落格

ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念,作為物件的模板。通過class關鍵字,可以定義類,與多數傳統語言類似。

//定義類
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  toString() {
    return '(' + this.x + ', ' + this.y + ')';
  }
}

ES7新特性

Array.prototype.includes

  Array.prototype.includes用法都容易和簡單。它是一個替代indexOf,開發人員用來檢查陣列中是否存在值,indexOf是一種尷尬的使用,因為它返回一個元素在陣列中的位置或者-1當這樣的元素不能被找到的情況下。所以它返回一個數字,而不是一個布林值。開發人員需要實施額外的檢查。在ES6,要檢查是否存在值你需要做一些如下圖所示小技巧,因為他們沒有匹配到值,Array.prototype.indexOf返回-1變成了true(轉換成true),但是當匹配的元素為0位置時候,該陣列包含元素,卻變成了false。
let arr = ['react', 'angular', 'vue']

// WRONG
if (arr.indexOf('react')) { // 0 -> evaluates to false, definitely as we expected
  console.log('Can use React') // this line would never be executed
}

// Correct
if (arr.indexOf('react') !== -1) {
  console.log('Can use React')
}

或者使用一點點hack 位運算子~使程式碼更加緊湊一些,因為~(位異或)對任何數字相當於-(a + 1):

let arr = ['react', 'angular', 'vue']

// Correct
if (~arr.indexOf('react')) {
  console.log('Can use React')
}

在ES7中使用includes程式碼如下:

let arr = ['react', 'angular', 'vue']

// Correct
if (arr.includes('react')) {
  console.log('Can use React')
}

還能在字串中使用includes:

let str = 'React Quickly'

// Correct
if (str.toLowerCase().includes('react')) {  // true
  console.log('Found "react"')  
}

除了增強了可讀性語義化,實際上給開發者返回布林值,而不是匹配的位置。

includes也可以在NaN(非數字)使用。最後 ,includes第二可選引數fromIndex,這對於優化是有好處的,因為它允許從特定位置開始尋找匹配。
更多例子:

console.log([1, 2, 3].includes(2)) // === true)
console.log([1, 2, 3].includes(4)) // === false)
console.log([1, 2, NaN].includes(NaN)) // === true)
console.log([1, 2, -0].includes(+0)) // === true)
console.log([1, 2, +0].includes(-0)) // === true)
console.log(['a', 'b', 'c'].includes('a')) // === true)
console.log(['a', 'b', 'c'].includes('a', 1)) // === false)

總而言之,includes在一個數組或者列表中檢查是否存在一個值,給任何開發人員帶來簡單性。

Exponentiation Operator(求冪運算)

  求冪運算大多數是做一些數學計算,對於3D,VR,SVG還有資料視覺化非常有用。在ES6或者早些版本,不得不建立一個迴圈,建立一個遞迴函式或者使用Math.pow,如果忘記了什麼是指數,當你有相同數字(基數)自相相乘多次(指數)。例如,7的3次方是7*7*7

所以在ES6/2015ES,你能使用Math.pow建立一個短的遞迴箭頭函式:

calculateExponent = (base, exponent) => base*((--exponent>1)?calculateExponent(base, exponent):base)
console.log(calculateExponent(7,12) === Math.pow(7,12)) // true
console.log(calculateExponent(2,7) === Math.pow(2,7)) // true

現在在ES7 /ES2016,以數學嚮導的開發者可以使用更短的語法:

let a = 7 ** 12
let b = 2 ** 7
console.log(a === Math.pow(7,12)) // true
console.log(b === Math.pow(2,7)) // true

開發者還可以操作結果:

let a = 7
a **= 12
let b = 2
b **= 7
console.log(a === Math.pow(7,12)) // true
console.log(b === Math.pow(2,7)) // true

許多ES新特性是從其他語言(CoffeeScript,Ruby,python等)模仿而來的

ES8新特性

Object.values/Object.entries

  Object.valuesObject.entries是在ES2017規格中,它和Object.keys類似,返回陣列型別,其序號和Object.keys序號對應。類似python中的dict.iteritems()。

Object.values,Object.entriesObject.keys各自項返回是陣列,相對應包括key,value或者可列舉特定物件property/attribute

在ES8 /ES2017之前,Javascript開發者需要迭代一個物件的自身屬性時候不得不用Object.keys,通過迭代且使用obj[key]獲取value值返回一個數組,很挫的:

let obj = {a: 1, b: 2, c: 3}
Object.keys(obj).forEach((key, index)=>{
  console.log(key, obj[key])
})

而使用ES6/ES2015 中for/of稍微好點:

let obj = {a: 1, b: 2, c: 3}
for (let key of Object.keys(obj)) {
  console.log(key, obj[key])
}

Object.values返回物件自身可以迭代屬性值(values)為陣列型別。我們最好使用Array.prototype.forEach迭代它,結合ES6的箭頭函式隱形返回值:

let obj = {a: 1, b: 2, c: 3}
Object.values(obj).forEach(value=>console.log(value)) // 1, 2, 3

或者實用for/of:

let obj = {a: 1, b: 2, c: 3}
for (let value of Object.values(obj)) {
  console.log(value)
}
// 1, 2, 3

·Object.entries·,在另一方面,將會返回物件自身可迭代屬性key-value對陣列(作為一個數組),他們(key-value)分別以陣列存放陣列中:

let obj = {a: 1, b: 2, c: 3}
JSON.stringify(Object.entries(obj))
"[["a",1],["b",2],["c",3]]"

可以使用ES6/ES2015解構,從這巢狀陣列中分別宣告key和value

let obj = {a: 1, b: 2, c: 3}
Object.entries(obj).forEach(([key, value]) => {
 console.log(`${key} is ${value}`)
})
// a is 1, b is 2, c is 3

同樣使用ES6for/of(畢竟全部都是陣列)遍歷Object.entries返回來的結果值:

let obj = {a: 1, b: 2, c: 3}
for (let [key, value] of Object.entries(obj)) {
  console.log(`${key} is ${value}`)
}
// a is 1, b is 2, c is 3

現在從物件中提取values和key-value pairs 變得非常容易了。Object.valuesObject.entries這種方式不想之前Object.keys(自身屬性key+順序相同)結合for/of(ES6)一起,我們不僅僅可以提取他們還可以迭代他們。

String padding(字串填充)

String.prototype.padStartString.prototype.padEnd在javascript字元操作是一個不錯的體驗,幫助避免依賴而外的庫。
padStart()在開始部位填充,返回一個給出長度的字串,填充物給定字串,把字串填充到期望的長度。從字串的左邊開始(至少大部分西方語言),一個經典例子是使用空格建立列:
console.log('react'.padStart(10).length)         // "       react" is 10
console.log('backbone'.padStart(10).length)         // "  backbone" is 10

它對於財務方面非常有用:

console.log('0.00'.padStart(20))            
console.log('10,000.00'.padStart(20))    
console.log('250,000.00'.padStart(20)) 

如果是為會計做賬之類的,這個很實用,帳做的很整齊