首頁  >  文章  >  web前端  >  JavaScript ES6的迭代器與生成器介紹

JavaScript ES6的迭代器與生成器介紹

巴扎黑
巴扎黑原創
2017-08-21 09:44:201088瀏覽

這篇文章主要介紹了深入理解ES6的迭代器與生成器,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧

本文介紹了深入理解ES6的迭代器與生成器,分享給大家,具體如下:

##循環語句的問題


var colors = ["red", "green", "blue"];
for(var i=0; i<colors.length; i++){
  console.log(colors[i]);
}

在ES6之前,這種標準的for循環,透過變數來追蹤陣列的索引。如果多個循環嵌套需要追蹤多個變量,程式碼複雜度會大大增加,也容易產生錯用循環變量的bug。

迭代器的出現旨在消除這種複雜性並減少循環中的錯誤。

什麼是迭代器

我們先感受一下用ES5語法模擬建立一個迭代器:


function createIterator(items) {
  var i = 0;
  
  return { // 返回一个迭代器对象
    next: function() { // 迭代器对象一定有个next()方法
      var done = (i >= items.length);
      var value = !done ? items[i++] : undefined;
      
      return { // next()方法返回结果对象
        value: value,
        done: done
      };
    }
  };
}

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

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: undefiend, done: true}"
// 之后所有的调用都会返回相同内容
console.log(iterator.next()); // "{ value: undefiend, done: true}"

以上,我們透過呼叫createIterator()函數,傳回一個對象,這個物件存在一個next()方法,當next()方法被呼叫時,返回格式{ value: 1, done : false}的結果物件。


因此,我們可以這麼定義:迭代器是擁有next()方法的特殊對象,每次呼叫next()都會傳回一個結果物件。

借助這個迭代器對象,我們來改造剛開始那個標準的for循環【暫時先忘記ES6的for-of循環新特性】:


var colors = ["red", "green", "blue"];
var iterator = createIterator(colors);
while(!iterator.next().done){
  console.log(iterator.next().value);
}

what?,消除循環變數而已,需要搞這麼麻煩,程式碼上不是得不償失了嗎?


#並非如此,畢竟createIterator()只要寫一次,就可以一直重複使用。不過ES6引入了生成器對象,可以讓創建迭代器的過程變得更簡單。

什麼是生成器

#產生器是一種傳回迭代器的函數,透過function關鍵字後的星號(*)來表示,函數中會用到新的關鍵字yield。


function *createIterator(items) {
  for(let i=0; i<items.length; i++) {
    yield items[i];
  }
}

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

// 既然生成器返回的是迭代器,自然就可以调用迭代器的next()方法
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: undefiend, done: true}"
// 之后所有的调用都会返回相同内容
console.log(iterator.next()); // "{ value: undefiend, done: true}"

上面,我們用ES6的生成,大大簡化了迭代器的建立過程。我們給生成器函數createIterator()傳入一個items數組,函數內部,for循環不斷從數組中生成新的元素放入迭代器中,每遇到一個yield語句循環都會停止;每次調用迭代器的next ()方法,循環便繼續運轉並停止在下一條yield語句處。

生成器的建立方式

生成器是函數:


function *createIterator(items) { ... }

可以用函數表達式方式書寫:


let createIterator = function *(item) { ... }

也可以加入到物件中,ES5風格物件字面量:


let o = {
  createIterator: function *(items) { ... }
};

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

ES6風格的物件方法簡寫方式:


let o = {
  *createIterator(items) { ... }
};

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

#可迭代物件

在ES6中,所有的集合對象(數組、Set集合及Map集合)和字串都是可迭代對象,可迭代對像都綁定了預設的迭代器。

來了來了,姍姍來遲的ES6循環新特性for-of:


var colors = ["red", "green", "blue"];
for(let color of colors){
  console.log(color);
}

for-of循環,可作用在可迭代在物件上,正是利用了可迭代物件上的預設迭代器。大致過程是:for-of迴圈每執行一次都會呼叫可迭代物件的next()方法,並將迭代器傳回的結果物件的value屬性儲存在變數中,迴圈將繼續執行此過程直到傳回物件的done屬性的值為true。

如果只需要迭代數組或集合中的值,用for-of迴圈取代for迴圈是個不錯的選擇。

存取預設迭代器

可迭代對象,都有一個Symbol.iterator方法,for-of循環時,透過呼叫colors數組的Symbol. iterator方法來取得預設迭代器的,這個過程是在JavaScript引擎背後完成的。

我們可以主動取得這個預設迭代器來感受一下:


let values = [1, 2, 3];
let iterator = values[Symbol.iterator]();

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}"

在這段程式碼中,透過Symbol.iterator取得了陣列values的預設迭代器,並用它來遍歷數組中的元素。在JavaScript引擎中執行for-of迴圈語句也是類似的處理過程。

用Symbol.iterator屬性偵測物件是否為可迭代物件:


#

function isIterator(object) {
  return typeof object[Symbol.iterator] === "function";
}

console.log(isIterable([1, 2, 3])); // true
console.log(isIterable(new Set())); // true
console.log(isIterable(new Map())); // true
console.log(isIterable("Hello")); // true

建立可迭代物件



##當我們在建立物件時,為Symbol.iterator屬性新增一個生成器,則可以將其變成可迭代物件:
  • let collection = {
      items: [],
      *[Symbol.iterator]() { // 将生成器赋值给对象的Symbol.iterator属性来创建默认的迭代器
        for(let item of this.items) {
          yield item;
        }
      }
    };
    
    collection.items.push(1);
    collection.items.push(2);
    collection.items.push(3);
    
    for(let x of collection) {
      console.log(x);
    }
  • 內建迭代器

  • ES6中的集合對象,陣列、Set集合和Map集合,都內建了三種迭代器:

    ##### #entries() 傳回一個迭代器,其值為多個鍵值對。如果是數組,第一個元素是索引位置;如果是Set集合,第一個元素與第二個元素一樣,都是值。 ############values() 傳回一個迭代器,其值為集合的值。 ############keys() 傳回一個迭代器,其值為集合中的所有鍵名。如果是數組,則傳回的是索引;如果是Set集合,則傳回的是值(Set的值同時用作鍵和值)。 ###

不同集合的默认迭代器

每个集合类型都有一个默认的迭代器,在for-of循环中,如果没有显式指定则使用默认的迭代器。按常规使用习惯,我们很容易猜到,数组和Set集合的默认迭代器是values(),Map集合的默认迭代器是entries()。

请看以下示例:


let colors = [ "red", "green", "blue"];
let tracking = new Set([1234, 5678, 9012]);
let data = new Map();

data.set("title", "Understanding ECMAScript 6");
data.set("format", "print");

// 与调用colors.values()方法相同
for(let value of colors) {
  console.log(value);
}

// 与调用tracking.values()方法相同
for(let num of tracking) {
  console.log(num);
}

// 与调用data.entries()方法相同
for(let entry of data) {
  console.log(entry);
}

这段代码会输入以下内容:

"red"
"green"
"blue"
1234
5678
9012
["title", "Understanding ECMAScript 6"]
["format", "print"]

for-of循环配合解构特性,操纵数据会更方便:


for(let [key, value] of data) {
  console.log(key + "=" + value);
}

展开运算符操纵可迭代对象


let set = new Set([1, 2, 3, 4, 5]),
  array = [...set];
  
console.log(array); // [1,2,3,4,5]

展开运算符可以操作所有的可迭代对象,并根据默认迭代器来选取要引用的值,从迭代器读取所有值。然后按返回顺序将它们依次插入到数组中。因此如果想将可迭代对象转换为数组,用展开运算符是最简单的方法。

以上是JavaScript ES6的迭代器與生成器介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn