[譯]JavaScript ES6迭代器指南

前言

EcmaScript 2015 (又稱ES6)提供一個全新的迭代器的概念,它容許咱們在語言層面上定義一個(有限或無限的)序列。es6

暫時先拋開它。咱們對於for循環以及它的兄弟for-in循環,都已經十分的熟悉。後者能夠被用來幫助咱們理解迭代器。數組

jsfor (var key in table) {
  console.log(key + ' = ' + table[key]);
}

對於for-in循環,它有許多的問題。可是最大的問題,即是它不保證迭代的順序。可是當咱們使用ES6迭代器時,這個問題就迎刃而解了。函數

for-of

for-of是ES6中的新語法,用來配合迭代器。oop

jsfor (var key of table) {
  console.log(key + ' = ' + table[key]);
}

使用for-of,咱們獲得的是一個能夠保證順序的迭代。爲了讓一個對象能夠被迭代器所迭代,對象須要實現一個「迭代協議」,即擁有一個Symbol.iterator屬性。這個屬性會被for-of所使用,在咱們的例子中,它就是table[Symbol.iterator]性能

Symbol.iterator也是在ES6中新增的內容,咱們會在另外一篇文章中詳細討論。在這裏,咱們只需認爲它是對象的一個特殊屬性,而且永遠不會和其餘普通屬性產生衝突。優化

table[Symbol.iterator]的值,必須是一個符合「迭代協議」的函數,即它須要返回一個相似於{ next: function () {} }的對象。this

jstable[Symbol.iterator] = function () {
   return {
    next: function () {}
  }
}

而後,在for-of循環每次調用next()函數時,它須要返回一個相似於{value: …, done: [true/false]}的對象。因此,一個迭代器的完整實現相似於以下的例子:rest

jstable[Symbol.iterator] = function () {
  var keys = Object.keys(this).sort();
  var index = 0;

  return {
    next: function () {
      return {
        value: keys[index], done: index++ >= keys.length
      };
    }
  }
}

惰性執行

迭代器容許咱們在第一次調用next()函數以後,再執行相應的邏輯。在上面的例子裏,當咱們調用迭代器的瞬間,咱們就馬上執行了排序和取值的工做。可是,若是next()函數永遠不被調用的話,咱們就浪費了性能。因此讓咱們來優化它:code

jstable[Symbol.iterator] = function () {
  var _this = this;
  var keys = null;
  var index = 0;

  return {
    next: function () {
      if (keys === null) {
        keys = Object.keys(_this).sort();
      }

      return {
        value: keys[index], done: index++ >= keys.length
      };
    }
  }
}

for-offor-in的差異對象

理解for-offor-in之間的差異,是十分重要的。如下是一個簡單的,可是很是好的解釋差異的例子:

jsvar list = [3, 5, 7];
list.foo = 'bar';

for (var key in list) {
  console.log(key); // 0, 1, 2, foo
}

for (var value of list) {
  console.log(value); // 3, 5, 7
}

正如所見的,for-of循環僅打印出了數組中的值,忽略了其餘屬性。這是由於數組的迭代器只返回其中預期的元素。

內置迭代器

StringArrayTypedArrayMapSet都是內置迭代器,由於它們的原型中都有一個Symbol.iterator方法。

jsvar string = "hello";

for (var chr of string) {
  console.log(chr); // h, e, l, l, o
}

解構賦值

解構操做一樣也接受一個迭代器:

jsvar hello = 'world';
var [first, second, ...rest] = [...hello];
console.log(first, second, rest); // w o ["r","l","d"]

無限迭代器

只要永遠不返回done: true,就實現了一個無限迭代器。固然,須要極力避免出現這種狀況。

jsvar ids = {
  *[Symbol.iterator]: function () {
    var index = 0;

    return {
      next: function () {
        return { value: 'id-' + index++, done: false };
      }
    };
  }
};

var counter = 0;

for (var value of ids) {
  console.log(value);

  if (counter++ > 1000) { // let's make sure we get out!
    break;
  }
}

Generator函數

若是你還不瞭解ES6 generator 函數,請參考MDN文檔。簡而言之,generator函數是當前被談論最多的ES6特性,它是一個能夠暫時退出,而且稍後從新進入繼續執行的函數。在屢次的進入中,它的上下文(綁定的變量)是會被保存的。generator函數自身就是一個迭代器,來看下面的例子:

jsfunction* list(value) {
  for (var item of value) {
    yield item;
  }
}

for (var value of list([1, 2, 3])) {
  console.log(value);
}

var iterator = list([1, 2, 3]);

console.log(typeof iterator.next); // function
console.log(typeof iterator[Symbol.iterator]); // function

console.log(iterator.next().value); // 1

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

因此,咱們能夠使用generator函數重寫咱們上面的迭代器:

jstable[Symbol.iterator] = function* () {
  var keys = Object.keys(this).sort();

  for (var item of keys) {
    yield item;
  }
}

最後

迭代器給JavaScript中的循環,generator函數和值序列(value series)帶來了一個新的維度。你能夠使用它,定義一個類中,它的值的排序方式,也能夠用經過其來建立一個惰性的或無限的序列,等等。

原文地址

https://strongloop.com/strongblog/introduction-to-es6-iterators/

相關文章
相關標籤/搜索