深刻理解ES6之迭代器與生成器

迭代器

迭代器 iterator,在 Javascript 中,迭代器是一個對象(也可稱做爲迭代器對象),它提供了一個 next() 方法,用來返回迭代序列中的下一項。函數

next 方法的定義,next 方法是一個函數,執行後返回一個對象包含兩個屬性:{ done: [boolean], value: [any] }ui

function makeIterator(array) {
  var nextIndex = 0
  return {
    next() {
      return nextIndex < array.length ? 
        { value: array[nextIndex++], done: false } : 
        { done: true }
    }
  }
}

// iterator 是一個迭代器對象
var iterator = makeIterator([10, 20, 30])
iterator.next() // {value: 10, done: false}
iterator.next() // {value: 20, done: false}
iterator.next() // {value: 30, done: false}
iterator.next() // {done: true}
複製代碼

可迭代對象

可迭代對象必須實現一個 @@iterator 方法,也就是說在這個對象或者它的原型鏈上必須有一個方法名是 Symbol.iterator 的方法,當調用這個方法時它返回一個迭代器對象。this

可迭代對象的表現形式爲,能夠使用 for...of 循環解構賦值拓展運算符(spread)yield* 這些語法來調用 Symbol.iterator 函數。也就是說這些語法糖在被調用時本質上都是在調用 Symbol.iterator 函數。spa

內置可迭代對象

String,Array,TypedArray,Map,Set,函數的arguments對象,NodeList對象都是內置的可迭代對象,他們的原型對象中都有一個 Symbol.iterator 方法。code

// 可迭代對象
let iterable = [10, 20, 30]
// 繼承自原型鏈
Symbol.iterator in iterable // true
iterable.hasOwnProperty(Symbol.iterator) // false

for(let value of iterable){
    console.log(value)
}
// 10
// 20
// 30
複製代碼

自定義可迭代對象

字面量對象 let o = {} 默認沒有 Symbol.iterator 方法,可是咱們在對象上自定義一個 @@iterator 方法,此時字面量對象也能夠使用 for...of循環,拓展運算符等等語法糖。對象

// 字面量對象默認是不可迭代對象
// 自定義對
var myIterable = {}
myIterable[Symbol.iterator] = function(){
  return {
    arr: [10, 20, 30],
    next: function(){
      if(this.arr.length > 0){
        return {value: this.arr.shift(), done: false}
      }else{
        return {done: true}
      }
    }
  }
}
[...myIterable] // [10, 20, 30]
複製代碼

生成器

生成器 generator,在 Javascript 中生成器是一個函數(也可稱做生成器函數),它能夠做爲建立迭代器的工廠函數。生成器函數的返回值是一個迭代器對象,同時這個對象也是一個可迭代對象。繼承

funtion* name(){ //statement } 這種聲明方式能夠定義一個生成器函數。ip

生成器函數的語法規則是,調用一個生成器函數並不會立刻執行它裏面的語句,而是返回一個這個生成器的 迭代器(iterator)對象。當這個迭代器的 next() 方法被首次(後續)調用時,其內的語句會執行到第一個(後續)出現yield的位置爲止,yield 後緊跟迭代器要返回的值。或者若是用的是 yield*(多了個星號),則表示將執行權移交給另外一個生成器函數(當前生成器暫停執行)。調用 next() 方法時,若是傳入了參數,那麼這個參數會做爲上一條執行的 yield 語句的返回值。原型鏈

// 生成器函數
function* generator(i){
    yield i + 1
    var y = yield 'foo'
    yield y
}

var iterator = generator(10) // 此時生成器函數不執行,返回一個迭代器

iterator.next()   // {value: 11, done: false} 
iterator.next()   // {value 'foo', done: false}
iterator.next(10) // {value: 10, done: false},將10賦值給上一條 yield 'foo' 左側的值,即 y = 10,返回 y
iterator.next()   // {done: true}
複製代碼

既然 生成器函數 能夠建立 迭代器對象,咱們來試着將前面的例子用生成器函數的形式重寫試試看。generator

// 生成器函數
function* makeIterator(array) {
  for (let i = 0; i < array.length; i++) {
    yield array[i]
  }
}

// 迭代器對象,實現和上文同樣的功能
var iteratorByGen = makeIterator([10, 20, 30])
iteratorByGen.next() // {value: 10, done: false}
iteratorByGen.next() // {value: 20, done: false}
iteratorByGen.next() // {value: 30, done: false}
iteratorByGen.next() // {done: true}
複製代碼

從上面的代碼咱們能夠看到,利用生成器函數來建立一個迭代器對象的方式相比於以前咱們普通函數建立的方式更加簡潔,也更加清晰的代表調用生成器函數返回的是一個迭代器對象。除此以外還有什麼區別呢。

上文已經提到,生成器函數返回的是一個 可迭代的迭代器對象,這是什麼意思呢?看下代碼就明白了。

// 生成器函數建立的迭代器對象
Symbol.iterator in iteratorByGen // true
[...iteratorByGen] // [10, 20, 30]

// 普通函數建立的迭代器對象
Symbol.iterator in iterator // false
[...iterator] // Uncaught TypeError: iterator is not iterable
複製代碼

綜上所述,咱們能夠肯定的說 生成器函數是建立迭代器對象的語法糖 ,經過生成器函數咱們能夠用很簡潔清晰的語法建立一個可迭代的迭代器對象。

相關文章
相關標籤/搜索