1. 程式人生 > 實用技巧 >Iterator 和 for...of

Iterator 和 for...of

Iterator 的作用有三個:

  1. 是為各種資料結構,提供一個統一的、簡便的訪問接
  2. 是使得資料結構的成員能夠按某種次序排列
  3. 是 ES6 創造了一種新的遍歷命令for...of迴圈,Iterator 介面主要供for...of消費

部署方式

原生就具有iterator的資料結構

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函式的 arguments 物件
  • NodeList 物件

ES6 規定,預設的 Iterator 介面部署在資料結構的Symbol.iterator屬性,或者說,一個數據結構只要具有Symbol.iterator屬性,就可以認為是“可遍歷的”(iterable)。Symbol.iterator屬性本身是一個函式

,就是當前資料結構預設的遍歷器生成函式。執行這個函式,就會返回一個遍歷器。至於屬性名Symbol.iterator,它是一個表示式,返回Symbol物件的iterator屬性,這是一個預定義好的、型別為 Symbol 的特殊值,所以要放在方括號內

// 為一個物件直接部署
const obj = {
  [Symbol.iterator] : function () {
    return {
      next: function () {
        return {
          value: 1,
          done: true
        };
      }
    };
  }
};

一個數據結構執行的for..of.. 迴圈,就會執行該資料結構的[Symbol.iterator]()
obj[Symbol.iterator]()執行之後返回一個遍歷器物件,其中next()方法用來移動指標,定義每一次遍歷的操作
throw()方法
return()方法的使用場合是,如果for...of迴圈提前退出(通常是因為出錯,或者有break語句),就會呼叫return()方法。如果一個物件在完成遍歷前,需要清理或釋放資源,就可以部署return()方法。

function readLinesSync(file) {
  return {
    [Symbol.iterator]() {
      return {
        next() {
          return { done: false };
        },
        return() {
          file.close();
          return { done: true };
        }
      };
    },
  };
}

//以下都會呼叫return方法
// 情況一
for (let line of readLinesSync(fileName)) {
  console.log(line);
  break;
}

// 情況二
for (let line of readLinesSync(fileName)) {
  console.log(line);
  throw new Error();
}

物件部署iterator的方式

使用類

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

  [Symbol.iterator]() { return this; } // 使用for of 迴圈是預設呼叫此方法 返回當前的例項化物件,該物件有一個next方法

  next() { // 返回值定義了該iterator的操作
    var value = this.value;
    if (value < this.stop) {
      this.value++;
      return {done: false, value: value};// done: false 可以省略
    }
    return {done: true, value: undefined };// value: undefined 可以省略
  }
}

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

for (var value of range(0, 3)) {
  console.log(value); // 0, 1, 2
}

建構函式的原型鏈上部署

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;
      current = current.next;
      return { done: false, 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; // 修改指標指向 保證Iterator的功能
two.next = three;

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

類陣列部署

類似陣列的物件(存在數值鍵名和length屬性),部署 Iterator 介面,有一個簡便方法,就是Symbol.iterator方法直接引用陣列的 Iterator 介面。
或者直接轉換成為類陣列

  • 普通物件部署陣列的Symbol.iterator方法,並無效果
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// 或者
NodeList.prototype[Symbol.iterator] = [][Symbol.iterator];

[...document.querySelectorAll('div')] // 可以執行了