1. 程式人生 > 實用技巧 >總結部分常用ES6的語法及其簡單解析

總結部分常用ES6的語法及其簡單解析

let/const

let/const用於宣告變數,替代老語法的var關鍵字,它的用法與var類似,但是所宣告的變數只在所在的程式碼塊中有效。

let/constvar的主要區別:

  • 不允許重複宣告
  • 未定義就使用會報錯:let/const不存在變數提升現象
  • 暫時性死區:在程式碼塊內使用const命令let命令宣告變數之前,該變數都不可用

const

let不相同的是,const宣告的是一個只讀常量。

const的特點:

  • constlet不同,const聲明後必須立馬賦值,只宣告不賦值就會報錯
  • const是一個只讀常量,並不是變數的值不改變,而是變數指向的那個記憶體地址不改變。
    • 對於簡單型別的資料(數值、字串、布林值),就相當於常量
    • 對於複合型別的資料(陣列、物件),變數指向記憶體地址,const實際儲存的是指向實際資料的指標,它只能保證指標不變,不能保證,指標裡的內容不變,

解構賦值

多種解構

字串解構

字串也可以解構賦值。這是因為此時,字串被轉換成了一個類似陣列的物件。

const [a, b, c, d, e] = 'hello';

數值和布林值解構

解構賦值時,如果等號右邊是數值和布林值,則會先轉為物件。

let {toString: s} = 123;
s === Number.prototype.toString // true

let {toString: s} = true;
s === Boolean.prototype.toString // true

物件解構

物件的屬性沒有次序,變數必須與屬性同名,才能取到正確的值。

形式:const { x, y } = { x: 1, y: 2 }

預設:const { x, y = 2 } = { x: 1 }

改名:const { x, y: z } = { x: 1, y: 2 }

陣列解構

只要某種資料結構具有Iterator介面就可採用陣列形式的解構賦值

形式:const [x, y] = [1, 2]

預設:const [x, y = 2] = [1]

函式引數解構

//陣列引數解構
function add([x, y]){
  return x + y;
}

add([1, 2]); // 3

//物件引數解構且有預設值
function move({x = 0, y = 0} = {}) {
  return [x, y];
}

move({x: 3, y: 8}); // [3, 8]
move({x: 3}); // [3, 0]
move({}); // [0, 0]
move(); // [0, 0]

應用場景

  • 交換變數值:[x, y] = [y, x]

  • 返回函式多個值:const [x, y, z] = Func()

  • 定義函式引數:Func([1, 2])

  • 提取JSON資料:const { name, version } = packageJson

  • 定義函式引數預設值:function Func({ x = 1, y = 2 } = {}) {}

  • 遍歷Map結構:for (let [k, v] of Map) {}

  • 輸入模組指定屬性和方法:const { readFile, writeFile } = require("fs")

重點

  • 匹配模式:只要等號兩邊的模式相同,左邊的變數就可以賦給右邊
  • 解構賦值規則:只要右邊不是物件或者陣列,就會將其轉換為物件
  • 解構不成功時,變數的值為undifined
  • undifinednull無法轉為物件,因此無法賦值

箭頭函式

ES6 允許使用箭頭(=>)定義函式,簡化定義函式

var f = v => v;

// 等同於
var f = function (v) {
  return v;
};
  • 無引數:() => {}
  • 單個引數:x => {}
  • 多個引數:(x, y) => {}
  • 解構引數:({x, y}) => {}

重點

  • 函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。

  • 不可以當作建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。

  • 不可以使用arguments物件,該物件在函式體內不存在。如果要用,可以用 rest 引數代替。

陣列的擴充套件

擴充套件運算子(...):轉換陣列為用逗號分隔的引數序列([...arr],相當於rest/spread引數的逆運算)

Array.from():轉換具有Iterator介面的資料結構為真正陣列,返回新陣列

類陣列物件:包含length的物件Arguments物件NodeList物件

可遍歷物件:StringSet結構Map結構Generator函式

Array.of():轉換一組值為真正陣列,返回新陣列

copyWithin():把指定位置的成員複製到其他位置,返回原陣列

find():返回第一個符合條件的成員

findIndex():返回第一個符合條件的成員索引值

fill():根據指定值填充整個陣列,返回原陣列

keys():返回以索引值為遍歷器的物件

values():返回以屬性值為遍歷器的物件

entries():返回以索引值和屬性值為遍歷器的物件

陣列空位:ES6明確將陣列空位轉為undefined(空位處理規不一,建議避免出現)

擴充套件運算子的應用

克隆陣列:const arr = [...arr1]

合併陣列:const arr = [...arr1, ...arr2]

拼接陣列:arr.push(...arr1)

代替apply:Math.max.apply(null, [x, y]) => Math.max(...[x, y])

轉換字串為陣列:[..."hello"]

轉換類陣列物件為陣列:[...Arguments, ...NodeList]

轉換可遍歷物件為陣列:[...String, ...Set, ...Map, ...Generator]

與陣列解構賦值結合:const [x, ...rest/spread] = [1, 2, 3]

計算Unicode字元長度:Array.from("hello").length => [..."hello"].length

Set

  • 定義:類似於陣列的資料結構,成員值都是唯一且沒有重複的值
  • 宣告:const set = new Set(arr)
  • 入參:具有Iterator介面的資料結構
  • 屬性
    • constructor:建構函式,返回Set
    • size:返回例項成員總數
  • 方法
    • add():新增值,返回例項
    • delete():刪除值,返回布林
    • has():檢查值,返回布林
    • clear():清除所有成員
    • keys():返回以屬性值為遍歷器的物件
    • values():返回以屬性值為遍歷器的物件
    • entries():返回以屬性值和屬性值為遍歷器的物件
    • forEach():使用回撥函式遍歷每個成員

應用場景

  • 去重字串:[...new Set(str)].join("")
  • 去重陣列:[...new Set(arr)]Array.from(new Set(arr))
  • 集合陣列
    • 宣告:const a = new Set(arr1)const b = new Set(arr2)
    • 並集:new Set([...a, ...b])
    • 交集:new Set([...a].filter(v => b.has(v)))
    • 差集:new Set([...a].filter(v => !b.has(v)))
  • 對映集合
    • 宣告:let set = new Set(arr)
    • 對映:set = new Set([...set].map(v => v * 2))set = new Set(Array.from(set, v => v * 2))

重點難點

  • 遍歷順序:插入順序
  • 沒有鍵只有值,可認為鍵和值兩值相等
  • 新增多個NaN時,只會存在一個NaN
  • 新增相同的物件時,會認為是不同的物件
  • 新增值時不會發生型別轉換(5 !== "5")
  • keys()values()的行為完全一致,entries()返回的遍歷器同時包括鍵和值且兩值相等

WeakSet

  • 定義:和Set結構類似,成員值只能是物件
  • 宣告:const set = new WeakSet(arr)
  • 入參:具有Iterator介面的資料結構
  • 屬性
    • constructor:建構函式,返回WeakSet
  • 方法
    • add():新增值,返回例項
    • delete():刪除值,返回布林
    • has():檢查值,返回布林

應用場景

  • 儲存DOM節點:DOM節點被移除時自動釋放此成員,不用擔心這些節點從文件移除時會引發記憶體洩漏
  • 臨時存放一組物件或存放跟物件繫結的資訊:只要這些物件在外部消失,它在WeakSet結構中的引用就會自動消

重點難點

  • 成員都是弱引用,垃圾回收機制不考慮WeakSet結構對此成員的引用
  • 成員不適合引用,它會隨時消失,因此ES6規定WeakSet結構不可遍歷
  • 其他物件不再引用成員時,垃圾回收機制會自動回收此成員所佔用的記憶體,不考慮此成員是否還存在於WeakSet結構

Map

  • 定義:類似於物件的資料結構,成員鍵是任何型別的值
  • 宣告:const set = new Map(arr)
  • 入參:具有Iterator介面且每個成員都是一個雙元素陣列的資料結構
  • 屬性
    • constructor:建構函式,返回Map
    • size:返回例項成員總數
  • 方法
    • get():返回鍵值對
    • set():新增鍵值對,返回例項
    • delete():刪除鍵值對,返回布林
    • has():檢查鍵值對,返回布林
    • clear():清除所有成員
    • keys():返回以鍵為遍歷器的物件
    • values():返回以值為遍歷器的物件
    • entries():返回以鍵和值為遍歷器的物件
    • forEach():使用回撥函式遍歷每個成員

重點難點

  • 遍歷順序:插入順序
  • 對同一個鍵多次賦值,後面的值將覆蓋前面的值
  • 對同一個物件的引用,被視為一個鍵
  • 對同樣值的兩個例項,被視為兩個鍵
  • 鍵跟記憶體地址繫結,只要記憶體地址不一樣就視為兩個鍵
  • 新增多個以NaN作為鍵時,只會存在一個以NaN作為鍵的值
  • Object結構提供字串—值的對應,Map結構提供值—值的對應

WeakMap

  • 定義:和Map結構類似,成員鍵只能是物件
  • 宣告:const set = new WeakMap(arr)
  • 入參:具有Iterator介面且每個成員都是一個雙元素陣列的資料結構
  • 屬性
    • constructor:建構函式,返回WeakMap
  • 方法
    • get():返回鍵值對
    • set():新增鍵值對,返回例項
    • delete():刪除鍵值對,返回布林
    • has():檢查鍵值對,返回布林

應用場景

  • 儲存DOM節點:DOM節點被移除時自動釋放此成員鍵,不用擔心這些節點從文件移除時會引發記憶體洩漏
  • 部署私有屬性:內部屬性是例項的弱引用,刪除例項時它們也隨之消失,不會造成記憶體洩漏

重點難點

  • 成員鍵都是弱引用,垃圾回收機制不考慮WeakMap結構對此成員鍵的引用
  • 成員鍵不適合引用,它會隨時消失,因此ES6規定WeakMap結構不可遍歷
  • 其他物件不再引用成員鍵時,垃圾回收機制會自動回收此成員所佔用的記憶體,不考慮此成員是否還存在於WeakMap結構
  • 一旦不再需要,成員會自動消失,不用手動刪除引用
  • 弱引用的只是鍵而不是值,值依然是正常引用
  • 即使在外部消除了成員鍵的引用,內部的成員值依然存在

Promise

  • 定義:Promise是一種非同步程式設計的解決方案。從語法上來講,Promise是一個物件,他可以獲取非同步操作的的訊息。從本意上來講,Promise是一個承諾,承諾過一段時間後給你一個結果。Promise有三種狀態:pending(等待),fulfiled(成功),rejected(失敗),狀態一旦改變,就不會再變。
  • 狀態:
    • 進行中:pending
    • 已成功:fulfilled
    • 已失敗:rejected
  • 特點:
    • 物件的操作不受外界的影響
    • 一旦狀態改變,任何時候都會得到這個結果
  • 示例:
const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 非同步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});
  • 出參

    • resolve:將狀態從未完成變為成功,在非同步操作成功時呼叫,並將非同步操作的結果作為引數傳遞出去
    • reject:將狀態從未完成變為失敗,在非同步操作失敗時呼叫,並將非同步操作的錯誤作為引數傳遞出去
  • 方法:

    • then():分別指定resolvedrejected狀態的回撥函式

      • 第一引數:狀態變為resolved時呼叫
      • 第二引數:狀態變為rejected時呼叫
    • catch():用於指定發生錯誤時的回撥函式。

    • Promise.all():將多個例項包裝成一個新例項,返回全部例項狀態變更後的結果陣列(齊變更再返回)

      • 入參:具有Iterator介面的資料結構
      • 成功:只有全部例項狀態變成fulfilled,最終狀態才會變成fulfilled
      • 失敗:其中一個例項狀態變成rejected,最終狀態就會變成rejected
    • Promise.race():將多個例項包裝成一個新例項,返回全部例項狀態優先變更後的結果(先變更先返回)

      • 入參:具有Iterator介面的資料結構
      • 成功失敗:哪個例項率先改變狀態就返回哪個例項的狀態
    • Promise.resolve():將物件轉為Promise物件(等價於new Promise(resolve => resolve()))

      • Promise例項:原封不動地返回入參
      • Thenable物件:將此物件轉為Promise物件並返回(Thenable為包含then()的物件,執行then()相當於執行此物件的then())
      • 不具有then()的物件:將此物件轉為Promise物件並返回,狀態為resolved
      • 不帶引數:返回Promise物件,狀態為resolved
    • Promise.reject():將物件轉為狀態為rejected的Promise物件(等價於new Promise((resolve, reject) => reject()))

應用場景

  • 載入圖片

  • AJAX轉Promise物件

重點難點

  • 只有非同步操作的結果可決定當前狀態是哪一種,其他操作都無法改變這個狀態

  • 狀態改變只有兩種可能:從pending變為resolved、從pending變為rejected

  • 一旦新建Promise物件就會立即執行,無法中途取消

  • 不設定回撥函式,內部拋錯不會反應到外部

  • 當處於pending時,無法得知目前進展到哪一個階段

  • 例項狀態變為resolvedrejected時,會觸發then()繫結的回撥函式

  • resolve()reject()的執行總是晚於本輪迴圈的同步任務

  • then()返回新例項,其後可再呼叫另一個then()

  • then()執行中丟擲錯誤會被catch()捕獲

  • reject()的作用等同於丟擲錯誤

  • 例項狀態已變成resolved時,再丟擲錯誤是無效的,不會被捕獲,等於沒有丟擲

  • 例項狀態的錯誤具有冒泡性質,會一直向後傳遞直到被捕獲為止,錯誤總是會被下一個catch()捕獲

  • 不要在then()裡定義rejected狀態的回撥函式(不使用其第二引數)

  • 建議使用catch()捕獲錯誤,不要使用then()第二個引數捕獲

  • 沒有使用catch()捕獲錯誤,例項拋錯不會傳遞到外層程式碼,即不會有任何反應

  • 作為引數的例項定義了catch(),一旦被rejected並不會觸發Promise.all()catch()

  • Promise.reject()的引數會原封不動地作為rejected的理由,變成後續方法的引數

for...of迴圈

  • 定義:呼叫Iterator介面產生遍歷器物件(for-of內部呼叫資料結構的Symbol.iterator())

  • 遍歷字串for-in獲取索引for-of獲取.

  • 遍歷陣列for-in獲取索引for-of獲取

  • 遍歷Setfor-of獲取 => for (const v of set)

  • 遍歷Mapfor-of獲取鍵值對 => for (const [k, v] of map)for-of獲得到的是一個由構成的陣列

    let map = new Map().set('a', 1).set('b', 2);
    for (let pair of map) {
      console.log(pair);
    }
    // ['a', 1]
    // ['b', 2]
    
    for (let [key, value] of map) {
      console.log(key + ' : ' + value);
    }
    // a : 1
    // b : 2
    
  • 遍歷物件for-in獲取for-of需自行部署for-of並不能直接遍歷物件,如果直接遍歷會報錯。我們需要部署Iterator介面。遍歷物件還是使用for-in更好一點

    1. 使用Object.keys方法將物件的鍵名生成一個數組,然後遍歷這個陣列。
    for (var key of Object.keys(someObject)) {
      console.log(key + ': ' + someObject[key]);
    }
    
    1. 使用 Generator 函式將物件重新包裝一下。
    function* entries(obj) {
      for (let key of Object.keys(obj)) {
        yield [key, obj[key]];
      }
    }
    
    for (let [key, value] of entries(obj)) {
      console.log(key, '->', value);
    }
    // a -> 1
    // b -> 2
    // c -> 3
    
  • for-in區別

    • 有著同for-in一樣的簡潔語法,但不會像for-in一樣會遍歷原型上的屬性。
    • for-of會以新增順序來進行遍歷,而不會像for-in以任意順序遍歷
    • 不同於forEach(),它可與breakcontinuereturn配合使用
    • 提供遍歷所有資料結構的統一操作介面

搬運文章

ES6入門教程

Promise不會??看這裡!!!史上最通俗易懂的Promise!!!

近一萬字的ES6語法知識點補充