ES6之遍歷語法

 

如何使用遍歷的

咱們暫且以數組爲例,javascript提供了多種遍歷數組的方法,最開始咱們可能習慣使用for循環:javascript

for (var index = 0; index < myArray.length; index++){
     console.log(myArray[index]);
}

這種寫法比較麻煩,因而數組內提供了forEach方法:java

myArray.forEach(function (value) {
    console.log(value);
});

這段代碼看起來更加簡潔,但這種方法也有一個小缺陷:你不能使用break語句中斷循環,也不能使用return語句返回到外層函數。
for...in循環(其實for...in循環是爲了遍歷對象而設計的,並不適合遍歷數組,切記!切記!切記!重要的事情說三遍!!!)es6

for (var index in myArray) { // 千萬別這樣作
    console.log(myArray[index]);
}

for...in循環便利數組絕對是一個糟糕的選擇,緣由以下:數組

  • 在這段代碼中,賦給index的值不是實際的數字,而是字符串「0」、「1」、「2」,此時極可能在無心之間進行字符串算數計算,例如:「2」 + 1 == 「21」,這給編碼過程帶來極大的不便。數據結構

  • 做用於數組的for-in循環體除了遍歷數組元素外,還會遍歷自定義屬性。舉個例子,若是你的數組中有一個可枚舉屬性myArray.name,循環將額外執行一次,遍歷到名爲「name」的索引。就連數組原型鏈上的屬性都能被訪問到。ide

  • 最讓人震驚的是,在某些狀況下,這段代碼可能按照隨機順序遍歷數組元素。函數

  • 簡而言之,for-in是爲普通對象設計的,你能夠遍歷獲得字符串類型的鍵,所以不適用於數組遍歷。this

強大的for-of循環編碼

for (var value of myArray) {
    console.log(value);
}

優勢:spa

  • 這是最簡潔、最直接的遍歷數組元素的語法
  • 這個方法避開了for-in循環的全部缺陷
  • 與forEach()不一樣的是,它能夠正確響應break、continue和return語句

其實不只能夠便利數組,ES6新增長的數據結構set和map也可使用for-of循環來進行遍歷

var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
  console.log(e);
}
// Gecko
// Trident
// Webkit

var es6 = new Map();
es6.set("edition", 6);
es6.set("committee", "TC39");
es6.set("standard", "ECMA-262");
for (var [name, value] of es6) {
  console.log(name + ": " + value);
}
// edition: 6
// committee: TC39
// standard: ECMA-262

上面代碼演示瞭如何遍歷 Set 結構和 Map 結構。值得注意的地方有兩個,首先,遍歷的順序是按照各個成員被添加進數據結構的順序。其次,Set 結構遍歷時,返回的是一個值,而 Map 結構遍歷時,返回的是一個數組,該數組的兩個成員分別爲當前 Map 成員的鍵名和鍵值。
甚至使用for-of循環亦能夠遍歷字符串:

for (var chr of "") {
  alert(chr);
}

深刻理解

1.Iterator(遍歷器)的概念

遍歷器(Iterator)就是這樣一種機制。它是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要部署Iterator接口,就能夠完成遍歷操做(即依次處理該數據結構的全部成員)。

Iterator的做用有三個:一是爲各類數據結構,提供一個統一的、簡便的訪問接口;二是使得數據結構的成員可以按某種次序排列;三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。

Iterator的遍歷過程是這樣的。

  • (1)建立一個指針對象,指向當前數據結構的起始位置。也就是說,遍歷器對象本質上,就是一個指針對象。

  • (2)第一次調用指針對象的next方法,能夠將指針指向數據結構的第一個成員。

  • (3)第二次調用指針對象的next方法,指針就指向數據結構的第二個成員。

  • (4)不斷調用指針對象的next方法,直到它指向數據結構的結束位置。

每一次調用next方法,都會返回數據結構的當前成員的信息。具體來講,就是返回一個包含value和done兩個屬性的對象。其中,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束。

在ES6中,有些數據結構原生具有Iterator接口(好比數組),即不用任何處理,就能夠被for...of循環遍歷,有些就不行(好比對象)。緣由在於,這些數據結構原生部署了Symbol.iterator屬性,另一些數據結構沒有。凡是部署了Symbol.iterator屬性的數據結構,就稱爲部署了遍歷器接口。調用這個接口,就會返回一個遍歷器對象。

在ES6中,有三類數據結構原生具有Iterator接口:數組、某些相似數組的對象、Set和Map結構。對於這三類數據結構,不用本身寫遍歷器生成函數,for...of循環會自動遍歷它們。除此以外,其餘數據結構(主要是對象)的Iterator接口,都須要本身在Symbol.iterator屬性上面部署,這樣纔會被for...of循環遍歷。

2.計算生成的數據結構

有些數據結構是在現有數據結構的基礎上,計算生成的。好比,ES6的數組、Set、Map 都部署瞭如下三個方法,調用後都返回遍歷器對象。

  • entries() 返回一個遍歷器對象,用來遍歷[鍵名, 鍵值]組成的數組。對於數組,鍵名就是索引值;對於 Set,鍵名與鍵值相同。Map 結構的 Iterator 接口,默認就是調用entries方法。
  • keys() 返回一個遍歷器對象,用來遍歷全部的鍵名。
  • values() 返回一個遍歷器對象,用來遍歷全部的鍵值。

這三個方法調用後生成的遍歷器對象,所遍歷的都是計算生成的數據結構。

 

 

屏幕快照 2017-06-07 下午3.46.23.png


值得注意的是ES6中的新數據結構set和map原生提供的除了這三個遍歷器生成函數以外,還有一個遍歷方法forEach()。切記不要將遍歷器生成函數和遍歷方法混爲一談

 

let map = new Map([
  ['f','no'],
  ['t','true']
]);
for (let[key,value] of map.entries()){
    console.log(key,value);
}

//等同於使用map.entries()
for (let[key,value] of map){
    console.log(key,value);
}

3.原生javascript對象的遍歷

前面咱們提到過那些沒有部署過Symbol.iterator屬性的數據結構也便是不具有Iterator接口的的對象是不能使用for...of方法遍歷的。經過Gnenrator函數爲這類對象加上這個Iterator接口就能夠了。

function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

  for (let propKey of propKeys) {
    yield [propKey, obj[propKey]];
  }
}

let jane = { first: 'Jane', last: 'Doe' };

for (let [key, value] of objectEntries(jane)) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

上面代碼中,對象jane原生不具有 Iterator 接口,沒法用for...of遍歷。這時,咱們經過 Generator 函數objectEntries爲它加上遍歷器接口,就能夠用for...of遍歷了。加上遍歷器接口的另外一種寫法是,將 Generator 函數加到對象的Symbol.iterator屬性上面。

function* objectEntries() {
  let propKeys = Object.keys(this);

  for (let propKey of propKeys) {
    yield [propKey, this[propKey]];
  }
}

let jane = { first: 'Jane', last: 'Doe' };

jane[Symbol.iterator] = objectEntries;

for (let [key, value] of jane) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

除了for...of循環之外,擴展運算符(...)、解構賦值和Array.from方法內部調用的,都是遍歷器接口。這意味着,它們均可以將 Generator 函數返回的 Iterator 對象,做爲參數。

function* numbers () {
  yield 1
  yield 2
  return 3
  yield 4
}

// 擴展運算符
[...numbers()] // [1, 2]

// Array.from 方法
Array.from(numbers()) // [1, 2]

// 解構賦值
let [x, y] = numbers();
x // 1
y // 2

// for...of 循環
for (let n of numbers()) {
  console.log(n)
}
// 1
// 2

總結

總而言之,ES6提供一個強大的遍歷方法for-of,對於那些原生具有Iterator接口的數據結構,能夠直接使用,而不具有具有Iterator接口的對象,須要部署Symbol.iterator屬性才能使用。

做者:大闖仔 連接:https://www.jianshu.com/p/b9e31305bcbb 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索