es6-迭代器Iterator

for 循環語句來迭代數據時,須要初始化一個變量來記錄每一次的迭代位置,但嵌套循環時就會變得繁瑣。因而,es6引入了迭代器和 for...of 的概念來簡化數據迭代操做。git

基本概念

迭代器是一種特殊的對象,它具備 next() 方法,每次調用返回一個結果對象。該對象包含兩個屬性,value表示下一次返回的值,done表示迭代是否結束,爲 bool值。咱們模擬實現產生迭代器的函數:es6

function createIterator(items) {
  let i = 0;
  return {
    next() {
      let done = i >= items.length;
      let value = !done ? items[i++] : undefined;

      return { done, value };
    }
  };
}

let iterator = createIterator([1, 2, 3]);

console.log(iterator.next());  // { done: false, value: 1 }
console.log(iterator.next());  // { done: false, value: 2 }
console.log(iterator.next());  // { done: false, value: 3 }
console.log(iterator.next());  // { done: true, value: undefined }
複製代碼

可迭代對象是指具備 Symbol.iterator 屬性的對象,該屬性值是一個返回迭代器的函數。在es6中,全部集合對象(數組,Set 集合和 Map 集合)和字符串都是可迭代對象,都有默認的迭代器,即有 Symbol.iterator 屬性值。github

咱們能夠爲對象增長 Symbol.iterator 屬性來自定義建立一個可迭代對象:數組

// 省略createIterator代碼

let obj = {
  items: [],
  push(x) {
    this.items.push(x);
    return this.items;
  },
  [Symbol.iterator]() {
    return createIterator(this.items);
  }
};
複製代碼

對於可迭代對象的遍歷es6給咱們提供了 for...of 語句,來看一個例子:函數

let arr = [1, 2, 3];
for (let x of arr) {
  console.log(x);
}
// 1
// 2
// 3
複製代碼

其實 for...of 每一次循環會調用可迭代對象的 Symbol.iterator 方法生成的迭代器的 next() 方法,並把結果對象的value值賦給變量,直到done的值爲true 。咱們用 for...of 訪問自定義的迭代對象:post

// ...

obj.push(1).push(2);

for (let x of obj) {
  console.log(x);
}

// 1
// 2

複製代碼

內建迭代器

爲了更好的訪問數組,Set 集合和 Map 集合,es6爲這3種對象提供了內置迭代器:ui

  • entries(): 返回一個迭代器,值爲包含鍵和值兩個元素的數組
  • keys(): 返回一個迭代器,值爲集合的鍵
  • values(): 返回一個迭代器,值爲集合的值

咱們來看數組的例子:this

let arr = [1, 2, 3];

for (let key of arr.keys()) {
  console.log(key);
}
// 0
// 1
// 2

for (let value of arr.values()) {
  console.log(value);
}
// 1
// 2
// 3

for (let entry of arr.entries()) {
  console.log(entry);
}
// [ 0, 1 ]
// [ 1, 2 ]
// [ 2, 3 ]
複製代碼

Map 集合的結果和上面相似,只是Set 集合的鍵和值都是同樣的,因此keys()values() 是等價的。spa

不一樣的集合有本身默認的迭代器。數組和 Set 使用 values() 方法返回的迭代器,而Map 集合是用 entries() 方法:code

let map = new Map([['name', 'wozien'], ['age', 23]]);

for (let entry of map) {
  console.log(entry);
}

// [ 'name', 'wozien' ]
// [ 'age', 23 ]
複製代碼

可用數組解構的方式來處理迭代的返回值:

let map = new Map([['name', 'wozien'], ['age', 23]]);

for (let [key, value] of map) {
  console.log(key + ' ' + value);
}

// name wozien
// age 23
複製代碼

在以前咱們能夠用展開運算符 (...)Set 集合轉爲一個數組,其實展開運算符可做用於任何可迭代對象,它會把迭代器 next() 方法的返回值按順序插入到數組中:

let map = new Map([['name', 'wozien'], ['age', 23]]);
let arr = [...map];

console.log(arr); // [ [ 'name', 'wozien' ], [ 'age', 23 ] ]
複製代碼

做用於自定義的迭代對象:

// 省略createIterator代碼

let obj = {
  [Symbol.iterator]() {
    return createIterator([1, 2, 3]);
  }
};

let arr = [...obj];

console.log(arr);  // [ 1, 2, 3 ]
複製代碼

生成可迭代對象

上面的除了手動實現 createIterator() 函數外,es6爲咱們提供了生成器(Generator),方便咱們生成迭代器。經過在 function 關鍵字後的星號(*)來表示,在函數體內用 yield 關鍵字控制迭代器 next() 的返回值:

function* createIterator() {
  yield 1;
  yield 2;
  yield 3;
}

let iterator = createIterator();

console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
複製代碼

經過生成器生成的迭代器每次調用 next()執行函數代碼時 ,每次執行 yield 語句完後就會自動中止執行。直到再次調用 next() 方法纔會繼續執行。

function* createIterator() {
  console.log(1);
  yield;
  console.log(2);
  yield;
  console.log(3);
}

let iterator = createIterator();

console.log(iterator.next());  // 1 { value: undefined, done: false }
console.log(iterator.next());  // 2 { value: undefined, done: false }

複製代碼

有了生成器,咱們能夠改寫自定義的可迭代對象:

let obj = {
  items: [],
  push(x) {
    this.items.push(x);
    return this.items;
  },
  [Symbol.iterator]: function*() {
    for (let item of this.items) {
      yield item;
    }
  }
};

obj.push(1).push(2);

for (let x of obj) {
  console.log(x);
}

// 1 2
複製代碼

或者能夠用es6定義對象函數的方式:

let obj = {
  // ...
  *[Symbol.iterator]() {
    for (let item of this.items) {
      yield item;
    }
  }
};
複製代碼

更多關於生成器的說明,參考下一篇es6-生成器Generator

參考

ES6 系列之迭代器與 for of

>>>原文地址

相關文章
相關標籤/搜索