ES6(十五)—— lterator

目錄

  • ES6如何讓不支持遍歷的結構可遍歷?數據結構

    • 將全部的做者名稱取出來
    • for-of須要Object.entries進行轉化
  • 規定函數

    • 可迭代協議 —— 去找對象上面有沒有Symbol.iterator屬性
    • 迭代器協議 —— 怎麼個迭代方式?返回無參函數next,next返回一個對象包含done和value屬性
  • Generator 和 lterator 結合使用
  • 案例
  • ES6-ES10學習版圖

ES6如何讓不支持遍歷的結構可遍歷?

let authors = {
  allAuthors: {
    fiction: [
      'Agatha Christie',
      'J. K. Rowling',
      'Dr. Seuss'
    ],
    scienceFiction: [
      'Neal Stephenson',
      'Arthur Clarke',
      'Isaac Asimov',
      'Robert Heinlein'
    ],
    fantasy: [
      'J. R. R. Tolkien',
      'J. K. Rowling',
      'Terry Pratchett'
    ]
  }
}

將全部的做者名稱取出來

ES5的作法post

allAuthors進行遍歷,看看取值狀況學習

for(let [k,v] of Object.entries(authors.allAuthors)){
  console.log(k,v)
}
//fiction (3) ["Agatha Christie", "J. K. Rowling", "Dr. Seuss"]
//scienceFiction (4) ["Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein"]
// fantasy (3) ["J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]

// 因此正確的寫法是:
let r = []
for(let [k,v] of Object.entries(authors.allAuthors)){
  r = r.concat(v)
}
console.log(r)
//["Agatha Christie", "J. K. Rowling", "Dr. Seuss", "Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein", "J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]

每次都須要遍歷,並且還要對obj不支持遍歷的對象進行改造。
並且還須要知道authors的內部結構。this

咱們的目標是:spa

  • 寫法優雅
  • 數據結構種的內置邏輯不須要關心
let r = []
for(let v of authors){
    console.log(v)
    r.push(v)
}

因此ES6的寫法prototype

//固定寫法
authors[Symbol.iterator] = function () {
  // 輸入 this,對象自己
  let allAuthors = this.allAuthors
  let keys = Reflect.ownKeys(allAuthors)
  console.log(keys) // ["fiction", "scienceFiction", "fantasy"]
  let values = [] // 是key的值
  return {
    // 必須返回一個方法
    next () {
      console.log(values)
      //一開始values.length是0,若是是0就進入循環過程
      if (!values.length) {
        if (keys.length) { 
          values = allAuthors[keys[0]]
          keys.shift() //永遠取第一個元素,用完以後進行彈出
        }
      }
      // 必須返回兩個值
      return {
        done: !values.length,
        value: values.shift()
      }
    }
  }
}

let r = []
for(let v of authors){
  r.push(v)
}
console.log(r)
//["Agatha Christie", "J. K. Rowling", "Dr. Seuss", "Neal Stephenson", "Arthur Clarke", "Isaac Asimov", "Robert Heinlein", "J. R. R. Tolkien", "J. K. Rowling", "Terry Pratchett"]

for-of須要Object.entries進行轉化

let obj = {
    1: "hello",
    "a":"hi"
};

for(let i of obj){console.log(i,obj[i]);}
//報錯:Uncaught TypeError: obj is not iterable

let obj1 = Object.entries(obj);
console.log(obj1) //[Array(2), Array(2)]

for(let [k,v] of obj1){
    console.log(k,v)
}
//1 hello
//a hi

規定

可迭代協議 —— 去找對象上面有沒有Symbol.iterator屬性

  • 可迭代協議容許 JavaScript 對象去定義或定製它們的迭代行爲, 例如(定義)在一個 for…of 結構中什麼值能夠被循環(獲得)。一些內置類型都是內置的可迭代類型而且有默認的迭代行爲, 好比 Array or Map, 另外一些類型則不是 (好比Object) 。
  • 爲了變成可迭代對象, 一個對象必須實現 @@iterator 方法, 意思是這個對象(或者它原型鏈 prototype chain 上的某個對象)必須有一個名字是 Symbol.iterator 的屬性
  • 這裏參考 遍歷 —— for-of(ES6新增)

迭代器協議 —— 怎麼個迭代方式?返回無參函數nextnext返回一個對象包含donevalue屬性

PS: 這個結構是否是和 Generator特別像?

若是 next 函數返回一個非對象值(好比falseundefined) 會展現一個 TypeError (「iterator.next() returned a non-object value」) 的錯誤code

//固定寫法
authors[Symbol.iterator] = function () {
  // 輸入 this,對象自己
  // 輸出 返回值(格式要求)
  return {
    // 必須返回一個next方法
    next () {
      // 必須返回兩個值
      return {
        done: false, // boolean false-遍歷沒有結束 true-遍歷結束
        value: 1 // 當前遍歷的項目的值
      }
    }
  }
}

Generator 和 lterator 結合使用

想了解Generator,參考文章 ES6(十四)—— Generator對象

//可迭代協議 加*就是Generator了
authors[Symbol.iterator] = function * () {
  // 輸入 this,對象自己
  let allAuthors = this.allAuthors
  let keys = Reflect.ownKeys(allAuthors)
  console.log(keys) // ["fiction", "scienceFiction", "fantasy"]
  let values = [] // 是key的值
  // 無線循環,若是退出以後,會自動停止退出的
  while(1){
    if(!values.length){
      if(keys.length){
        values = allAuthors[keys[0]]
        keys.shift()
        yield values.shift()
      }else{
        // 退出循環
        return false
      }
    }else{
      yield values.shift()
    }
  }
}

案例

  • 場景:你我協同開發一個任務清單應用
  • 迭代器的意義: 外部不用去關心內部的結構,直接進行遍歷就能夠拿到所有數據。
// each方法,是todos內部暴露的方法
// 更好的是把todos直接變成一個可迭代的對象
const todos = {
  life: ['吃飯', '睡覺', '打豆豆'],
  learn: ['語文', '數學', '外語'],
  work: ['喝茶'],

  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for( const item of all) {
      callback(item)
    }
  },
  [Symbol.iterator]: function() {
    const all = [...this.life, ...this.learn, ...this.work]
    let index = 0
    return {
      next: function () {
        return {
          value: all[index],
          done: index++ >= all.length
        }
      }
    }
  }
}

todos.each(function(item){
  console.log(item)
})
// 吃飯
// 睡覺
// 打豆豆
// 語文
// 數學
// 外語
// 喝茶

for(const item of todos){
  console.log("for-of: " +item)
}

使用Generator函數實現Iterator方法,對上面的案例進行改進blog

const todos = {
  life: ['吃飯', '睡覺', '打豆豆'],
  learn: ['語文', '數學', '外語'],
  work: ['喝茶'],

  each: function (callback) {
    const all = [].concat(this.life, this.learn, this.work)
    for( const item of all) {
      callback(item)
    }
  },
  [Symbol.iterator]: function * () {
    const all = [...this.life, ...this.learn, ...this.work]
    for(const item of all) {
      yield item
    }
  }
}

for(const item of todos){
  console.log(item)
}
// 吃飯
// 睡覺
// 打豆豆
// 語文
// 數學
// 外語
// 喝茶

ES6-ES10學習版圖

相關文章
相關標籤/搜索