ES6生成器(Generator)和迭代器(Iterator)

引言

我们常常需要遍历一个对象,例如访问数组里的值,字符串中的值,arguments里的值等。我么常用let...of...来对对象进行遍历,而这个方法的底层实现就是迭代器(Iterator)

迭代器

ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)。而迭代器的本质,就是内部实现了一个next()方法,返回不同的数据。

生具备 Iterator 接口的数据结构如下:Array, String, map, set, string (对象不可以哦!!!)

let arr = [1, 2 ,3]
for(let item of arr){
  console.log(arr);
} // 可以正常输出,1, 2, 3

let obj = {name: 'huang'}
for(let item of obj){
  console.log(item);
} // 不可以正常输出,报错:obj is not iterable
// 可以在控制台打印一下,查看二者是否具备[Symbol.iterator]属性

迭代器的使用

获取具体的迭代器对象,然后调用迭代器身上的next()方法,就可以逐步拿到数据

let arr = [1, 2, 3]
console.log(arr);
let Iter = arr[Symbol.iterator]() 
console.log(Iter.next()); // {value: 1, done: false}
console.log(Iter.next()); // {value: 2, done: false}
console.log(Iter.next()); // {value: 3, done: false}
console.log(Iter.next()); // {value: undefined, done: true}

自定义迭代器

总结:只要给一个对象实现了Symbol.iterator方法,let...of...就可以迭代。这个迭代的本质,就是借用了里面的next()方法来实现。

class MyArray {
  constructor() {
    for (let i = 0; i < arguments.length; i++) {
      this[i] = arguments[i];
    }
    this.length = arguments.length;
  }
  [Symbol.iterator]() {
    let index = 0;
    let that = this;
    return {
      next: function () {
        return index < that.length ? 
          {value: that[index++], done: false}:
          {value: undefined, done: true}
      }

    }
  }
}

let arr = new MyArray(1, 2, 3);
let Iter = arr[Symbol.iterator]()
console.log(Iter.next());
console.log(Iter.next());
console.log(Iter.next());
console.log(Iter.next());

for(let item of arr){
  console.log(item); // 1, 2, 3
}

// 如果知识让你实现next方法,就不用自己定义数组类了,直接定义一个构造函数即可。

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

let Iter = MyIterator([1, 2, 3, 4, 5]);

把普通对象转化为可迭代对象

普通对象默认没有配置Iterator,所以不可以迭代

let obj = {
 name: "lnj",
  age: 34,
  gender: "man",
  [Symbol.iterator]() {
    let keys = Object.keys(this);
    let index = 0;
    let that = this;
    return {
      next() {
        return index < keys.length ?
          { value: that[keys[index++]], done: false } :
          { value: undefined, done: true }
      }
    }
  }
}
let it = obj[Symbol.iterator]();
console.log(it.next());
console.log(it.next());
console.log(it.next());
console.log(it.next());
for (let value of obj) {
  console.log(value); // 'lnj', 34, 'man' 当然返回什么可以自己定义
}

把类数组对象转化为可迭代对象

注意:普通对象不能直接利用Symbol.iterator,因为普通对象没有索引值,所以Keys需要自己去获取

let obj = {
  0: 1,
  1: 2,
  2: 3,
  length: 3,
  [Symbol.iterator]: Array.prototype[Symbol.iterator]
}
for (let item of obj) {
  console.log(item); // 1 2 3
}

注意:let...of..., 解构赋值, 拓展运算符都是Iterator的应用场景,因为他们都需要迭代对象的内容

生成器

Generator 函数是 ES6 提供的一种异步编程解决方案,Generator 函数内部可以封装多个状态, 因此又可以理解为是一个状态机。只需要在普通函数的function后面加上*,该函数就会变为Generator 函数,通过yield定义状态,并且yield关键字可以让 Generator内部的逻辑能够切割成多个部分。yield可以通过调用迭代器的next函数,不断执行定义的状态

生成器可以定义多个状态,在不同的时机调用
生成器可以返回迭代器对象,可以用来简写迭代器的写法

基本用法

每一次调用迭代器的next方法,都会调用相应的yield声明的状态前的代码片段。
如果写了return,也是如此,只不过,后面的yield从此都不会被使用到了

function* test(){
   const res1 = yield 1;
   console.log(res1); // two
   const res2 = yield 2;
   console.log(res2); // three
   const res3 =yield 3;
   console.log(res3); // four
   
 }

 let iter = test();
 console.log(iter.next('one'));
 console.log(iter.next('two'));
 console.log(iter.next('three'));
 console.log(iter.next('four'));
function *test(){
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
}
let iter = test();
// 这里也可以直接用Let...of...进行遍历
// for(let key of iter){
//   console.log(key); // 1 2 3 4 5
// }
console.log(iter.next()); // { value: 1, done: false }
console.log(iter.next()); // { value: 2, done: false }
console.log(iter.next()); // { value: 3, done: false }
console.log(iter.next()); // { value: 4, done: false }
console.log(iter.next()); // { value: 5, done: false }
console.log(iter.next()); // { value: undefined, done: true }

返回多个结果

function* test(a, b) {
  yield a + b
  yield a - b
}
let Iter = test(1, 2)

console.log(Iter.next());  // {value: 3, done: false}
console.log(Iter.next());  // {value: -1, done: false}

用生成器使对象可遍历

上面把对象转化为可迭代对象是利用自己定义的next方法。但是,生成器本来就是用来生成迭代器的,所以可以使上面的写法更加简单

let obj = {
 name: "lnj",
  age: 34,
  gender: "man",
  [Symbol.iterator]: function* () {
    let index = 0;
    let keys = Object.keys(this)
    while(index < keys.length) {
      yield this[keys[index++]]
    }
    
  }
}
let it = obj[Symbol.iterator]();
console.log(it.next()); // {value: 'lnj', done: false}
console.log(it.next()); // {value: 34, done: false}
console.log(it.next()); // {value: 'man', done: false}
console.log(it.next()); // {value: undefined, done: true}
for (let value of obj) {
  console.log(value); // 'lnj', 34, 'man' 当然返回什么可以自己定义
}

Promise Generator async/await解决异步问题简要记录

问题的出现来自于回调地狱

function request(fn) {
  setTimeout(() => {
    fn('拿到的数据') // 发起了异步请求,拿到了数据,作为了fn的回调参数
  }, 3000);
}

request(function (data) {
  console.log('我是第一层', data);
  request(function (data) {
    console.log('我是第二层', data);
    request(function (data) {
      console.log('我是第三层', data);
    })
  })
})

Promise改写

看起来规整了一点

function request(){
 return new Promise((resolve, reject) => {
    setTimeout(() => resolve('拿到的数据'), 1000)
  })
}

request().then((data)=>{
  console.log('我是第一层', data);
  return request()
}).then((data)=>{
  console.log('我是第二层', data);
  return request()
}).then((data)=>{
  console.log('我是第三层', data);
  return request()
})

Generator改写

Generator也是ES6提出的解决异步编程的方案,和Promise不属于同一技术(我目前认为)

 function request(fn) {
   setTimeout(() => {
     fn('拿到的数据') // 发起了异步请求,拿到了数据,作为了fn的回调参数
   }, 3000);
 }

 function* test(){
   yield request(function (data) {console.log('我是第一层', data);})
   yield request(function (data) {console.log('我是第二层', data);})
   yield request(function (data) {console.log('我是第三层', data);})
 }

 let Iter = test();
 // 三秒后这三个会同时出现, 但是我就是可以控制它什么时候有,什么时候没有
 Iter.next()
 Iter.next()
 Iter.next()

Async/Await

简单理解:是Generator的语法糖, async替代*, await 替代yield, 并对其他的一些细节做了优化,例如Async函数的返回值是Promise包装过的,而Generator返回对象是Iterator。注意:await后如果异常了,需要自己去try...catch...,而Promise提供了catch方法,且Promise提供了更多的方法,例如Promise.all等

这种写法让人看起来最最舒服了!!!!

function request() {
  return new Promise(function (resolve, reject) {
    setTimeout(function () {
      resolve(2000);
    },1000)
  })

  // 当然也是可以不用return Promise对象的。。。会帮你转化为Promise
  // return 1

}
async function test() {
  let res1 = await request();
  console.log('第一层数据', res1);
  let res2 = await request();
  console.log('第二层数据', res2);

  let res3 = await request();
  console.log('第三层数据', res3);


}

let res = test()
console.log(res) // 返回的是Promise;
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值