ES6中的Iterator和for...of循環那些事

最近看阮一峯阮大神的ES6,剛剛看到Iterator和for...of循環這一章,小做筆記跟你們略微分享一下,不足之處還望你們多多指正es6

Iterator(遍歷器)就是一種機制;任何數據結構只要是部署了iterator接口,就能夠完成遍歷操做(即依次處理該數據的全部成員);數組

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

只要是一個對象部署了Symbol.interator接口,就能夠用for...of遍歷該對象,同時也能夠調用該接口的Symbol.interator方法調用next()方法對對象進行遍歷,不一樣的是for..of是對該對象的值的輸出,而next()返回的是對象。
例以下面的例子:函數

clipboard.png
若是調用next方法就是this

clipboard.png

調用next()方法會返回一個對象{value:value;done:true or false};直到對遍歷對象的值便利完成以後,next()方法會返回一個{value:undefined; done:true}表明已經遍歷完成,再調用next()方法也不會報錯可是依舊會返回{value:undefined; done:true};spa

有一些對象是原生就封裝好的Symbol.iterator接口,能夠直接調用,固然也能夠直接用for...of遍歷;
在ES6中,有三類數據結構原生具有Iterator接口:數組、某些相似數組的對象、Set和Map結構。
若是沒有原生的Symbol.iterator接口,想用for...of遍歷,就須要本身在該對象中部署Symbol.iterator接口,例如prototype

一個對象若是要有可被for...of循環調用的Iterator接口,就必須在Symbol.iterator的屬性上部署遍歷器生成方法(原型鏈上的對象具備該方法也可)。code

一個對象若是要有可被for...of循環調用的Iterator接口,就必須在Symbol.iterator的屬性上部署遍歷器生成方法(原型鏈上的對象具備該方法也可)。

class RangeIterator {
  constructor(start, stop) {
    this.value = start;
    this.stop = stop;
  }

  [Symbol.iterator]() { return this; }

  next() {
    var value = this.value;
    if (value < this.stop) {
      this.value++;
      return {done: false, value: value};
    } else {
      return {done: true, value: undefined};
    }
  }
}

function range(start, stop) {
  return new RangeIterator(start, stop);
}

for (var value of range(0, 3)) {
  console.log(value);
}//阮大神案例

上面代碼是一個類部署Iterator接口的寫法。Symbol.iterator屬性對應一個函數,執行後返回當前對象的遍歷器對象。
原型鏈上部署Symbol.iterator接口對象

function Obj(value){
  this.value = value;
  this.next = null;
}

Obj.prototype[Symbol.iterator] = function(){

  var iterator = {
    next: next
  };

  var current = this;

  function next(){
    if (current){
      var value = current.value;
      var done = current == null;
      current = current.next;
      return {
        done: done,
        value: value
      }
    } else {
      return {
        done: true
      }
    }
  }
  return iterator;
}

var one = new Obj(1);
var two = new Obj(2);
var three = new Obj(3);

one.next = two;
two.next = three;

for (var i of one){
  console.log(i)
}
// 1
// 2
// 3

對象內部部署接口

let obj = {
  data: [ 'hello', 'world' ],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
      next() {
        if (index < self.data.length) {
          return {
            value: self.data[index++],
            done: false
          };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

也就是說若是一個對象沒有Symbol.iterator接口,能夠在類自己上面部署,也能夠在原型連上部署,也能夠在對象內部部署

對於相似數組的對象(存在數值鍵名和length屬性),部署Iterator接口,有一個簡便方法,就是Symbol.iterator方法直接引用數組的Iterator接口。

NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

[...document.querySelectorAll('div')] // 能夠執行了

具體請看例子

let iterable = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); // 'a', 'b', 'c'
}

**可是請記住,這個方法僅僅適用於類數組對象,上面的也能夠直接用Array.from(iterable)轉換成數組來遍歷,例如

let arrayLike = { 
  length: 2, 
  0: 'a', 
  1: 'b'
 };

for (let x of Array.from(arrayLike)) {
  console.log(x);
}//a b

對於普通對象這兩個方法是無論用的,**例如

let iterable = {
  edition: 'a',
  writer: 'b',
  read: 'c',
  length: 3,
};
for (let item of Array.from(iterable)) {
  console.log(item); 
}

上面的代碼就會輸出三個undefined

let iterable = {
  edition: 'a',
  writer: 'b',
  read: 'c',
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (let item of iterable) {
  console.log(item); //報錯
}

上面的代碼就會拋異常的;

其實ES6不少地方都用的到Iterator這個接口,例如:

  1. 解構賦值

  2. 擴展運算符

  3. yield*

最後須要說下,字符串既是類數組對象同時本身也有原生Symbol.iterator接口能夠直接調用,例如
var str = "hell";
for(let v of str){
console.log(v);//"h","e","l","l"
}
對於字符串來講,for...of循環還有一個特色,就是會正確識別32位UTF-16字符。

for...of 區別於for循環,for循環比較麻煩,可是是最原始的方法;
for...of 區別於數組的forEach方法,由於forEach方法是從頭至尾執行,不會跳出,可是遇到break或者return,continue會跳出循環

有着同for...in同樣的簡潔語法,可是沒有for...in那些缺點。
不一樣用於forEach方法,它能夠與break、continue和return配合使用。
提供了遍歷全部數據結構的統一操做接口。

更詳細的請查看阮大神(http://es6.ruanyifeng.com/#docs/iterator)本章內容

相關文章
相關標籤/搜索