迭代器-生成器的学习

54 阅读4分钟

迭代器-生成器

什么是迭代器

/**
 * 迭代器: 是一种特殊对象
 * 所有的迭代器对象都有一个next()方法,每次调用都返回一个结果对象。
 * 结果对象有两个属性:一个是value,表示下一个将要返回的值;另一个是done,是否完成迭代
 */

const arrs = [1, 2, 3, 4, 5, 6]
// 创建一个迭代器访问数组
// const arrIterator = {
//   next() {
//     return { done: false, value: 1 }
//     return { done: false, value: 2 }
//     return { done: false, value: 3 }
//     return { done: false, value: 4 }
//     return { done: false, value: 5 }
//     return { done: false, value: 6 }
//     return { done: true, value: undefined }
//   }
// }
let index = 0
const arrIterator = {
  next() {
    if(index < arrs.length) {
      return { done: false, value: arrs[index++] }
    }else {
      return { done: true, value: undefined }
    }
  }
}

// done: 是否完成操作
console.log(arrIterator.next()) // { done: false, value: 1 }
console.log(arrIterator.next()) // { done: false, value: 2 }
console.log(arrIterator.next()) // { done: false, value: 3 }
console.log(arrIterator.next()) // { done: false, value: 4 }
console.log(arrIterator.next()) // { done: false, value: 5 }
console.log(arrIterator.next()) // { done: false, value: 6 }
console.log(arrIterator.next()) // { done: true, value: undefined }

生成迭代器的函数封装

function createArrayIterator(arr){
  let index = 0
  return {
    next() {
      if(index < arr.length){
        return { done: false, value: arr[index++] }
      }else{
        return { done: true, value: undefined }
      }
    }
  }
}

const arrs = [1,2,3]
const names = ['aaa','bbb','ccc']

const arrIterator = createArrayIterator(arrs)
console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());
console.log(arrIterator.next());

const nameIterator = createArrayIterator(names)
console.log(nameIterator.next());
console.log(nameIterator.next());
console.log(nameIterator.next());
console.log(nameIterator.next());

// 创建一个无限迭代器
function createNumberIterator() {
  let index = 0 // 注意: 这儿形成了闭包
  return {
    next() {
      return { done: false, value: index++ }
    }
  }
}
const numberIterator = createNumberIterator()
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());
console.log(numberIterator.next());

什么是可迭代对象

/**
 * 可迭代对象: 是一个特殊的对象
 * 必须有一个[Symbol.iterator]方法,内部并且返回一个迭代器(对象)
 */
const iterableObj = {
  names: ['aaa', 'bbb', 'ccc'],
  [Symbol.iterator]: function(){
    let index = 0
    return {
      next: () => { // 箭头函数不绑定this, this就为iterableObj
        if(index < this.names.length) {
          return { done: false, value: this.names[index++] }
        }else {
          return { done: true, value: undefined }
        }
      }
    }
  }
}
// iterableObj对象就是一个可迭代对象
console.log(iterableObj[Symbol.iterator]);

const iterator1 = iterableObj[Symbol.iterator]()
console.log(iterator1.next());
console.log(iterator1.next());
console.log(iterator1.next());
console.log(iterator1.next());

// 1.每次调用iterableObj[Symbol.iterator](),生成新的迭代器
// const iterator2 = iterableObj[Symbol.iterator]()
// console.log(iterator2.next());
// console.log(iterator2.next());
// console.log(iterator2.next());
// console.log(iterator2.next());


// 2.for...of 遍历时,必须是一个可迭代对象
/**
 * for...of 遍历可迭代对象时:
 * 内部实现: 先拿到可迭代对象的迭代器,然后自动调用next(),取返回对象的value属性值
 */
for(const value of iterableObj){
  console.log(value);
}

console.log(Symbol.iterator) // Symbol(Symbol.iterator)

原生内置可迭代对象

// 数组本身就是一个可迭代对象.
const names = ['aaa', 'bbb', 'ccc'] // new Array()

// const nameIterator = names[Symbol.iterator]()
// console.log(nameIterator.next());
// console.log(nameIterator.next());
// console.log(nameIterator.next());
// console.log(nameIterator.next());
// console.log(nameIterator.next());

// for(const item of names) {
//   console.log(item);
// }

// Map/Set
const set = new Set()
set.add(10)
set.add(100)
set.add(1000)

console.log(set[Symbol.iterator]);

for(const item of set) {
  console.log(item);
}

// 函数的arguments也是一个可迭代对象
function foo(x, y, z) {
  console.log(arguments[Symbol.iterator])
  for(const arg of arguments) {
    console.log(arg);
  }
}
foo(1, 2, 3)

// String Array Map Set argiments对象 NodeList集合 都是 可迭代对象

可迭代对象的应用场景

// 1.for...of 遍历可迭代对象

// 2.展开运算符,内部使用迭代器
const names = ['aaa', 'bbb', 'ccc']
const newNames = [ ...names ]
console.log(newNames)

// 特殊: 对象类型没有迭代器,不能用for...of
const obj = {
  name: 'Fhup',
  age: 18
}
// ES9(ES2018)新增: 对象展开,用的不是迭代器原理.
const newObj = { ...obj }
console.log(newObj);

// 3.解构语法
const [a,b,c, d = 'ddd'] = names
console.log(a, b, c, d);
// ES9新增: 解构内部原理也不是迭代器,使用普通for进行遍历赋值的.
const {age, name} = obj
console.log(name, age);

// 4.创建一些其他对象时
const set = new Set(names)
console.log(set)

const arr1 = Array.from(names)
console.log(arr1);

// 5.Promise.all(传入的names被Promise.resolve(names)转为promise对象)
Promise.all(names).then(res=>{
  console.log(res);
})

自定义类的可迭代性(监听中断)

// 1.创建一个类,所创建出来的对象都是可迭代对象
class Person {
  constructor(address, name, students) {
    this.address = address
    this.name = name
    this.students = students
  }
  entry(newStudent){
    this.students.push(newStudent)
  }

  [Symbol.iterator]() {
    let index = 0
    return {
      next: ()=>{ // 获取this,函数为箭头函数
        if(index < this.students.length) {
          return { done: false, value: this.students[index++] }
        }else {
          return { done: true, value: undefined }
        }
      },
      // 迭代器中断的监听.
      return: ()=>{
        console.log('迭代器提前终止了~')
        return { done: true, value: undefined }
      }
    }
  }
}

var p = new Person('12b-321', 'shehui', ['xiaohong', 'xiaozhang', 'ludehua'])
p.entry('zhoujielun')

/**
 * 2.在没有完全迭代的情况下中断:
 * 通过 break continue return throw 中断了循环操作. 或者解构时,没有解构所有的值
 * 想要监听中断,在迭代器中添加return方法.
 */
for(const stu of p) {
  console.log(stu);
  if(stu === 'ludehua') break // 导致迭代器中断
}


// 3.给函数增加可迭代对象
// function Person() {

// }
// Person.prototype[Symbol.iterator] = function() { // 添加到原型上
// }

什么是生成器函数

/**
 * 生成器函数: 是一个特殊的函数.
 * 1.生成器函数在function关键字后面加符号 * : function*
 * 2.控制函数的执行流程: 通过yield关键字
 */
function* foo() {
  console.log('函数开始执行');

  console.log('第一段', 'aaaaaaaa');
  yield

  console.log('第二段', 'bbbbbbb');
  yield
  
  console.log('第三段', 'cccccc');
  yield

  console.log('函数执行结束');
}

// 3.调用生成器函数时,给我们返回一个生成器对象
/**
 * 生成器: ES6新增的一种函数 控制/使用 的方案.
 * 灵活的控制函数什么时候执行,什么时候暂停执行.
 * 生成器事实上是一种特殊的迭代器.( 可以调next() )
 */
const generator = foo()

// next()开始执行第一段代码 (第一个yield之前的代码)
generator.next()
// 执行第二段代码
generator.next()

generator.next()
generator.next()
console.log('---------');
generator.next()
console.log('--------');

生成器函数的执行流程

/**
 * 当遇到yield时暂停函数的执行
 * 当遇到return时生成器停止执行
 */
function* foo() {
  console.log('函数开始执行');

  console.log('第一段', 'aaaaaaaa');
  yield 'one' // 返回值放在yield后面,类型可以为 表达式/函数

  console.log('第二段', 'bbbbbbb');
  yield 'two'
  
  console.log('第三段', 'cccccc');
  yield 'three'

  console.log('函数执行结束');
  return '123'
}

// 生成器事实上是一种特殊的迭代器,有返回值
const generator = foo()
console.log('返回值1:', generator.next());
console.log('返回值2:', generator.next());
console.log('返回值3:', generator.next());
console.log('返回值4:', generator.next());


生成器next传递参数

function* foo (index){
  console.log('函数开始执行');
  // 第一段想要传参,通过函数参数获取
  console.log('第一段', 'aaaaaaaa', ', index:', index);
  const num = yield 'one'
  
  // 第二段传入的参数获取从第一段的返回值中拿到
  console.log('第二段', 'bbbbbbb', ', num:', num);
  yield 'two'
  
  console.log('第三段', 'cccccc');
  yield 'three'

  console.log('函数执行结束');
  return '123'
}

// 生成器通过next()方法传递参数
const generator = foo(999)
generator.next()
generator.next(100)
generator.next()
generator.next()

生成器return终止执行

function* foo (){
  console.log('函数开始执行');
  console.log('第一段', 'aaaaaaaa');
  const num = yield 'one'
  
  console.log('第二段', 'bbbbbbb', ', num:', num);
  yield 'two'
  
  console.log('第三段', 'cccccc');
  yield 'three'

  console.log('函数执行结束');
  return '123'
}

const generator = foo()

console.log(generator.next());

// 第二段代码的执行,使用了return
// 相当于在一段代码后面加上了 return num; 提前终止生成器函数代码继续执行.
console.log(generator.return(15));

生成器throw抛出异常

function* foo() {
  console.log('函数开始执行');
  console.log('第一段', 'aaaaaaaa');
  // 对异常进行捕获,后面代码继续执行
  try {
    yield 'one'
  } catch (err) {
    console.log('捕获异常:',err);

    yield 'catch里yield' // 会执行
  }
  
  console.log('第二段', 'bbbbbbb');
  yield 'two'
  
  console.log('第三段', 'cccccc');
  yield 'three'

  console.log('函数执行结束');
  return '123'
}

const generator = foo()
console.log(generator.next());
// 执行第二段代码时,抛出异常
console.log(generator.throw('err message~'));

生成器替代迭代器

// 1.生成器替代迭代器
function* createArrayIterator(arr) {
  // 1.1.第一种写法
  // let index = 0
  // yield arr[index++]
  // yield arr[index++]
  // yield arr[index++]
  
  // 1.2.第二种写法
  // for(const item of arr) {
  //   yield item
  // }

  // 1.3.第三种写法 yield* + 可迭代对象 (第二种写法的语法糖)
  yield* arr
  
  // 迭代器
  // return {
  //   next: () => {
  //     if(index < arr.length){
  //       return { done: false, value: arr[index++] }
  //     }else {
  //       return { done: true, value: undefined }
  //     }
  //   }
  // }
}

const names = ['aaa', 'bbb', 'ccc']
const arrayIterator = createArrayIterator(names)
console.log(arrayIterator.next());
console.log(arrayIterator.next());
console.log(arrayIterator.next());
console.log(arrayIterator.next());


// 2.创建一个函数,函数迭代一个范围内的数字
console.log('--------------------------');
function* createRangeIterator(start, end) {
  // 2.1 第一种写法
  // for(start; start<=end; start++){
  //   yield start
  // }
  // 2.2 第二种写法
  while(start <= end){
    yield start++
  }

  // return {
  //   next() {
  //     if(start <= end){
  //       return { done: false, value: start++ }
  //     }else{
  //       return { done: true, value: undefined }
  //     }
  //   }
  // }
}

const rangeIterator = createRangeIterator(10, 20)
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());
console.log(rangeIterator.next());

// 3.创建一个类,所创建出来的对象都是可迭代对象
class Person {
  constructor(address, name, students) {
    this.address = address
    this.name = name
    this.students = students
  }
  entry(newStudent){
    this.students.push(newStudent)
  }

  /// 特殊的写法 注意: 加上分号 ; 
  foo = function(){
  };

  // [Symbol.iterator] = function*() {
  //   yield* this.students
  // }

  *[Symbol.iterator]() {
    yield* this.students
  }


}

const p = new Person('12b-321', 'shehui', ['xiaohong', 'xiaozhang', 'ludehua'])
p.entry('zhoujielun')

for(const stu of p) {
  console.log(stu);
}

异步代码的处理方案

function requestData(url) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(url)
    }, 2000);
  })
}


// 1.第一种方案: Promise中then的返回值来解决
// requestData("Fhup").then(res => {
//   return requestData(res + "aaa")
// }).then(res => {
//   return requestData(res + "bbb")
// }).then(res => {
//   console.log(res)
// })

// 2.第二种方案: Promise + generator实现
// function* getData() {
//   const res1 = yield requestData("Fhup") // Fhup
//   const res2 = yield requestData(res1 + "aaa") // Fhupaaa
//   const res3 = yield requestData(res2 + "bbb") // Fhupaaabbb
//   console.log(res3)
// }

// const generator = getData()
// generator.next().value.then(res => { // Fhup
//   generator.next(res).value.then(res => { // Fhupaaa
//     generator.next(res).value.then(res => { // Fhupaaabbb
//       generator.next(res) // Fhupaaabbb
//     })
//   })
// })


// 3.第三方包co自动执行
// TJ: co/n(nvm)/commander/express/koa(egg)
// const co = require('co')
// co(getData)


// 4.第四种方案: async/await 是 Promise + generator 的 语法糖
async function getData() { // 4.1 * 变为 async
  const res1 = await requestData("Fhup")
  const res2 = await requestData(res1 + "aaa") // 4.2 yield 变为 await
  const res3 = await requestData(res2 + "bbb")
  const res4 = await requestData(res3 + "ccc")
  console.log(res4)
}
getData()