ES6學習筆記(五)【解構賦值,Iterator】

風靈使發表於2019-04-06

簡介

在很多流行的程式語言裡,都有解構賦值的概念,比如PerlPython

ES6中也加入了類似的賦值語句,比以前的賦值操作更靈活,更快捷

正文

陣列的解構賦值很好理解,就是按照對應位置,對變數賦值:

let [a, b, c] = [1, 2, 3]

相當於

let a = 1
let b = 2
let c = 3

支援陣列巢狀,也支援等號兩邊結構不同的情況

如果變數對應不上值,這個變數就是undefined,如果一個值對應不到變數,那這個值被忽略

[a, b, c] = [1, 2]
// a,b為1,2   c由於匹配不到為undefined
[a, b] = [1, 2, 3]
// a,b為1,2   3由於沒有變數接收被忽略

基於解構賦值,函式也可以有多個返回值了:

function tuple() {
  return [1, 2]
}

let [first, second] = tuple()

解構賦值允許指定預設值,也允許使用rest引數(...)來接收不確定個數的引數:

let [firstName = "John", lastName = "Doe"] = []

let [a, b, ...rest] = [1, 2, 3, 4, 5]
a    // 1
b    // 2 
rest // [3, 4, 5]

與函式的rest引數一樣,rest後面不允許再有其他引數了

物件的解構賦值與陣列類似,只有在屬性名對應相同的情況下,右側屬性的值會被賦值給左側對應屬性的值

let {firstName: name, lastName} = {firstName: "John", lastName: "Doe"}

name      // "John"
lastName  // "Doe"
firstName // Uncaught ReferenceError: firstName is not defined

注意被賦值的是同名屬性的值,這裡是name 而不是firseName

lastName被賦值成功是因為這是變數的簡潔表示法,忘記了的回去看物件一節

其實陣列可以看做屬性名都是自然數的物件:

let {"0": a, "1": b} = {"0": 1, "1": 2}

a     // 1
b     // 2

這樣看起來會更好的理解物件的解構賦值,實際上行為與陣列的解構賦值是一致的

思考

這部分內容希望你都可以手動敲一遍,獨立思考

試著用解構賦值去交換兩個變數的值

如果不使用解構賦值,如何交換兩個變數的值呢?


let [a = 1] = [undefined]

let [b = 1] = [null]

上面程式碼中的a, b分別是什麼?試著自己解釋這個結果


let [x = y, y = 1] = []

上面程式碼的結果是什麼? 為什麼?


ES6中新增了一種遍歷語法:for (... of ...) 來支援所有可遍歷的資料結構

for-of 是基於iterator這個通用介面實現的

正文

在ES6以前,我們想遍歷一個陣列一般有三種方式:

  • for 迴圈
  • for ... in 語句
  • Array.prototype.forEach 方法

三種方式都有不同程度的缺陷

for迴圈寫法繁瑣,需要指定迴圈次數不超過陣列長度,否則就會有對應的報錯

for ... in 語句遍歷出的是鍵值,需要做一些轉換才能拿到資料,而且遍歷是無序的

forEach 方法是個不錯的方式,不過它不能中途使用break跳出迴圈,也不能return終止外層函式

基於這些缺陷,ES6新增了 for ... of 的語法,用於遍歷陣列,物件,字串,或者是SetMap等資料結構

for ... of 遍歷依賴 iterator 介面,只有部署了 iterator 介面的資料型別才可以被遍歷

iterator 介面是這些資料結構上的一個屬性:Symbol.iterator,它是一個函式

let iterator = [1, 2, 3][Symbol.iterator]()

iterator    //  Array Iterator {}
iterator.next()
// {value: 1, done: false}
iterator.next()
// {value: 2, done: false}
iterator.next()
// {value: 3, done: false}
iterator.next()
// {value: undefined, done: true}

Symbol.iterator顯然是一個Symbol型別值,在Symbol章節介紹過,必須使用方括號訪問

呼叫陣列上的 Symbol.iterator 方法,即可返回一個iterator遍歷器物件

這個 iterator 物件上只有一個常用方法 next ,每次呼叫 next 即可返回一個物件

返回的物件有兩個屬性,value :當前遍歷的值, done :是否遍歷完畢

這裡必須說明一下為什麼這個遍歷器的屬性名是一個很繞的Symbol型別

其實最開始是打算叫做 iterator() 的 ,可是考慮到已有的程式碼中,可能已經有人用這個名字命名了一些屬性

ES6如果直接使用這個名字會造成相容性問題,無奈之下只能使用絕對不可能重複的 [Symbol.iterator]()

for…of 語句會在內部直接呼叫遍歷物件上的 Symbol.iterator 方法

並且每次迴圈中自動呼叫返回值的next方法,然後將next方法返回值的value屬性值賦值給每次的迴圈變數

for (let a of arr){
  console.log(a)
}
//  1  2  3

正常情況下我們不會手動使用next方法去一個一個的拿到值

而是使用 for … of 方便的遍歷所有具有 Symbol.iterator 屬性的物件

思考

這部分內容希望你都可以手動敲一遍,獨立思考

let map = new Map([
  ['a', '1'],
  ['b', '2']
])

for (let a of map){
  console.log(a)
}

上面程式碼的輸出結果會是什麼?

如果想要每次只輸出map中每一項對應的值,應該怎麼改寫?


試著根據上面描述的for ... of實現方法,自己動手實現一個具有for ... of功能的function

接收一個可遍歷物件,並向命令列依次輸出遍歷結果

相關文章