javascript 高级编程系列 - 迭代器/生成器

1. 迭代器(Iterator)

  • 特殊对象,具有迭代过程的接口next()方法
  • 每次调用next()方法,返回一个结果对象
  • 结果对象有两个属性value(任何类型)和done(布尔类型)
  • 当集合的值返回完时,再次调用next()方法,结果对象的done属性值为true,value为迭代器最终返回的值。

使用javascript创建迭代器

function createIterator(items){
  let i = 0;
  return {
    next: () => {
      let done = (i >= items.length);
      let value = !done ? items[i++] : undefined;
      return { done, value };
    }
  };
}

const 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: undefined, done: true}

2. 生成器(Generator)

  • 生成器是一种返回迭代器的函数, 通过function关键字后面的*来表示
  • 函数中会用到yield关键字,并且只能在生成器内部使用,在嵌套函数中也不允许使用
  • 每执行完一条yield语句后,函数就会自动停止执行,直到再次调用迭代器的next()方法
  • 不能使用箭头函数来创建生成器

创建生成器

function *createIterator() {
  yield 1;
  yield 2;
  yield 3;
}
const iterator = createIterator();
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}

创建生成器函数表达式

const createIterator = function *(items){
  for (let i = 0; i < items.length; i++){
    yield items[i];
  }
}
const iterator = createIterator([1,2,3]);
for (let value of iterator){
  console.log(value);
}
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}

3. 可迭代对象与for-of循环

  • 可迭代对象具有Symbol.iterator属性,是一种与迭代器密切相关的对象。
  • Symbol.iterator属性通过指定的函数可以返回一个作用与附属对象的迭代器。
  • 所有集合对象(数组,Set集合,Map集合)以及字符串都是可迭代对象。
  • for-of 循环每执行一次就会调用可迭代对象的next()方法,并将迭代器返回的结果对象的value属性存储在
    一个变量中,循环持续这一过程,直到结果对象的done属性值为true
  • 生成器默认会为Symbol.iterator属性赋值,通过生成器创建的迭代器都是可迭代对象

生成器产生的迭代器是可迭代对象

const createIterator = function *(items){
  for (let i = 0; i < items.length; i++){
    yield items[i];
  }
}
const iterator = createIterator([1,2,3]);
for (let value of iterator){
  console.log(value); // 1, 2, 3
}

4. 访问默认迭代器

  • 可以通过Symbol.iterator来访问对象默认的迭代器
const values = [1, 2, 3];
const 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}
  • 检测对象是否为可迭代对象
function isIterable(object){
  return typeof object[Symbol.iterator] === 'function';
}
const setObj = new Set([1,2,3]);
console.log(isIterable(setObj)); // true

5. 创建可迭代对象

  • 通过给普通对象Symbol.iterator属性添加一个生成器,从而使其变为可迭代对象
const collection = {
  items: [1,2,3],
  *[Symbol.iterator]() {
    for (let item of this.items){
      yield item;
    }
  }
};

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

6. 内建迭代器

ES6中有3种类型的集合对象:数组,Map集合,Set集合,它们都内建了以下三种迭代器:

  • keys() 返回一个迭代器,其值为集合中所有的键名

  • values() 返回一个迭代器,其值为集合的值

  • entries() 返回一个迭代器,其值为多个键值对

  • 数组(默认迭代器为values())

let colors = ['red', 'green', 'blue'];

for (let entry of colors.entries()){
  console.log(entry); // [0, 'red'] [1, 'green'] [2, 'blue']
}
for (let key of colors.keys()){
  console.log(key); // 0, 1, 2
}
for (let value of colors.values()) {
  console.log(value); // 'red', 'green', 'blue'
}
// 使用默认迭代器
for( let color of colors){
  console.log(color); // 'red', 'green', 'blue'
}
  • Set集合(默认迭代器为values())
let numbers = new Set([1, 2, 3]);
for (let entry of numbers.entries()){
  console.log(entry); // [1, 1] [2, 2] [3, 3]
}
for (let key of numbers.keys()){
  console.log(key); //  1, 2, 3
}
for (let value of numbers.values()) {
  console.log(value); //1, 2, 3
}
// 使用默认迭代器
for (let number of numbers){
  console.log(number); // 1, 2, 3
}
  • Map集合(默认迭代器为entries())
let data = new Map([['name', 'mike'], ['age', '30']]);
for (let entry of data.entries()){
  console.log(entry); // ['name', 'mike'] ['age', '30']
}
for (let key of data.keys()){
  console.log(key); //  'name', 'age'
}
for (let value of data.values()) {
  console.log(value); // 'mike', '30'
}
// 使用默认迭代器
for (let [key, value] of data){
  console.log(key); // 'name', 'age'
  console.log(value); // 'mike', '30'
}
  • 字符串迭代器
const message = 'ab你好cd';
for (let c of message){
  console.log(c); // 'a', 'b', '你', '好', 'c', 'd'
}

7. 高级迭代器功能

  • 给迭代器传递参数
    通过给迭代器的next()方法传递参数,则这个参数值回替代生成器内部上一条yield语句的返回值
function *createIterator() {
  let first = yield 1;
  let second = yield first + 2;
  yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next());  // { value: 1, done: false }
console.log(iterator.next(4)); // { value: 6, done: false }
console.log(iterator.next(5)); // { value: 8, done: false }
console.log(iterator.next());  // { value: undefined, done: true }

注意:第一次调用next()方法无论传入什么参数都会被丢弃。由于传给next()方法的参数会替代上一次
yield的返回值,而在第一次调用next()方法前不会执行任何yield语句,因此在第一次调用next()方法
时传递参数是毫无意义的。

  • 在迭代器中抛出错误
    通过throw()方法,当迭代器恢复执行时可令其抛出一个错误
function *createIterator() {
  let first = yield 1;
  let second;
  try {
    second = yield first + 2;
  } catch (ex){
    second = 6;
  }
  yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next());  // { value: 1, done: false }
console.log(iterator.next(4)); // { value: 6, done: false }
console.log(iterator.throw(new Error("error"))); // { value: 9, done: false }
console.log(iterator.next());  // { value: undefined, done: true }

  • 生成器返回语句
    return 语句可以提前返回,也可以为最后一次next()方法调用指定一个返回值。
function *createIterator() {
  yield 1;
  return 42;
}
let iterator = createIterator();
console.log(iterator.next());  // { value: 1, done: false }
console.log(iterator.next());  // { value: 422, done: true }
console.log(iterator.next());  // { value: undefined, done: false }
  • 委托生成器
    在某些情况下,我们需要将两个迭代器合二为一,这时可以创建一个生成器,再给yield语句添加
    一个星号,就可以将生成的数据的过程委托给其他迭代器。
function *createIterator1() {
  yield 1;
  yield 2;
}
function *createIterator2() {
  yield 3;
  yield 4;
}
function *createCombinedIterator() {
  yield *createIterator1();
  yield *createIterator2();
  yield true;
}
const iterator = createCombinedIterator();
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: 4, done: false }
console.log(iterator.next());  // { value: true, done: false }
console.log(iterator.next());  // { value: undefined, done: false }

8. 异步任务执行器

  • 定义一个异步任务执行器
  • 创建一个包装器,封装异步操作
  • 定义一个生成器来编排包装的异步任务
  • 将生成器作为任务执行器函数参数执行
// 定义任务执行器
function run(taskDef){
  // 创建迭代器
  let task = taskDef();
  // 开始执行任务
  let result = task.next();

  // 循环调用next() 函数
  function step(){
    // 如果任务未完成,则继续执行
    if(!result.done){
      if(typeof result.value === 'function'){
        result.value(function(err, data){
          if(error){
            result = task.throw(err);
            return;
          }
          result = task.next(data);
          step();
        });
      }
      else{
        result = task.next(result.value);
        step();
      }
    }
  }
  
  // 开始迭代执行
  step();
}

// 定义异步任务函数
function readFile(filename){
  return function(callback){
    fs.readFile(filename, callback);
  };
}

// 执行任务
run(function *(){
  let contents = yield readFile('config.json');
  doSomething(contents);
  console.log('done');
});
posted @   箫笛  阅读(374)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示