深入理解ES6 迭代器(Iterator)和生成器(Genera

2023-04-26  本文已影响0人  抓住时间的尾巴吧

Set集合与Map集合
迭代器(Iterator)和生成器(Generator)
改进的数组功能

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

迭代器(Iterator)

1.什么是迭代器Iterator
2.集合对象迭代器

在ES6中,所有的集合对象(数组、Set集合及Map集合)和字符串都是可迭代对象,这些对象中都有默认的迭代器。ES6中新加入的特性for-of循环需要用到可迭代对象的这些功能:

const map = new Map([['name', 'Jony'], ['age', 25]]);
const mapIterator = map.entries();
mapIterator.next(); // " {done:  false,value: ['name', 'Jony']}
mapIterator.next(); // {done:  false,value: ['age', '25']}
mapIterator.next(); // {done:  true,value: undefined} 
3.什么是生成器Generator

生成器是一种返回迭代器的函数,通过function关键字后的星号(*)来表示,函数中会用到新的关键字yield。星号可以紧挨着function关键字,也可以在中间添加一个空格

// 生成器
function *createIterator() {
  yield 1;
  yield 2;
  yield 3;
}
// 生成器能像正规函数那样被调用,但会返回一个迭代器
let iterator = createIterator();
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
function *createIterator(items) {    
for (let i = 0; i < items.length; i++) {
  yield items[i];
  }
}
let 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 }"// 之后的所有调用
console.log(iterator.next()); // "{ value: undefined, done: true }"

【使用限制】

yield关键字只可在生成器内部使用,在其他地方使用会导致程序抛出错误

function *createIterator(items) {
  items.forEach(function(item) {       
    // 语法错误
    yield item + 1;
  });
}

【生成器对象的方法】

由于生成器本身就是函数,因而可以将它们添加到对象中。例如,在ES5风格的对象字面量中,可以通过函数表达式来创建生成器

var o = {
  createIterator: function *(items) {            
    for (let i = 0; i < items.length; i++) {
      yield items[i];
    }
  }
};
let iterator = o.createIterator([1, 2, 3]);

也可以用ES6的函数方法的简写方式来创建生成器,只需在函数名前添加一个星号(*)

var o = {
  *createIterator(items) {            
    for (let i = 0; i < items.length; i++) {
      yield items[i];
    }
  }
};
let iterator = o.createIterator([1, 2, 3]);
4.创建可迭代对象

默认情况下,开发者定义的对象都是不可迭代对象,但如果给Symbol.iterator属性添加一个生成器,则可将其变为可迭代对象

let collection = {
  items: [],
  *[Symbol.iterator]() {
    for (let item of items) {
      yield item;
    } 
  }
};

collection.items.push(1);
collection.items.push(2);
collection.items.push(3);

for (let x of collection) {
  console.log(x);
}
5.访问默认的迭代器
let values = [1, 2, 3];
let 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 }" //
6.字符串迭代器
const message = "A 𠮷 B";
const message = "A 中 B";
for (let i = 0; i < message.length; i++) {
console.log(message[i]); // A ()��()B // A () () () () B
}
for (let item of message) {
console.log(item);// A () 𠮷 () B
}
7.NodeList迭代器
const divs = document.getElementsByTagName('div');
for (let div of divs) {
  console.log(div.id);
}
8.展开运算符和非数组可迭代对象

通过展开运算符(...)可以把Set集合转换成一个数组

let set = new Set([1, 2, 3, 3, 3, 4, 5]),
array = [...set];
console.log(array); // [1,2,3,4,5]
let map = new Map([ ["name", "huochai"], ["age", 25]]),
array = [...map];
console.log(array); // [ ["name", "huochai"], ["age", 25]]
let smallNumbers = [1, 2, 3],
bigNumbers = [100, 101, 102],
allNumbers = [0, ...smallNumbers, ...bigNumbers];
console.log(allNumbers.length); // 7
console.log(allNumbers); // [0, 1, 2, 3, 100, 101, 102]
9.例子

例子A

let state = function*(){    
  while(1){
    yield 'A';
    yield 'B';
    yield 'C';
  }
}
let status = state();
console.log(status.next().value);//'A'
console.log(status.next().value);//'B'
console.log(status.next().value);//'C'
console.log(status.next().value);//'A'
console.log(status.next().value);//'B'

例子B

function *createIterator() {
    console.log('begin');
    let a = yield 1;
    console.log('a', a);
    let b = yield a + 2;
    console.log('b', b);
    yield b + 3;
    console.log('finish');
}
let iterator = createIterator();
iterator.next(0); // return {value: 1, done: false}
// begin
iterator.next(2); // return {value: 4, done: false}
// a 2
iterator.next(4);// return {value: 6, done: false}
// b 4
iterator.next(6);// return {value: undefined, done: true}
// finish

例子C

function *createIterator() {
  let first = yield 1;
  let second = yield first + 2; // 4 + 2
  yield second + 3; // 5 + 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 }"
10.在迭代器中抛出错误

除了给迭代器传递数据外,还可以给它传递错误条件。通过throw()方法,当迭代器恢复执行时可令其抛出一个错误。这种主动抛出错误的能力对于异步编程而言至关重要,也能提供模拟结束函数执行的两种方法(返回值或抛出错误),从而增强生成器内部的编程弹性。将错误对象传给throw()方法后,在迭代器继续执行时其会被抛出

function *createIterator() {
  let first = yield 1;
  let second = yield first + 2; // yield 4 + 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.throw(new Error("Boom"))); // 从生成器中抛出了错误
console.log(iterator.next(5)); // "{ value: undefined, done: true }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

捕获错误

function *createIterator() {
  let first = yield 1;
  let second; 
  try {
    second = yield first + 2; // yield 4 + 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("Boom"))); // "{ value: 9, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
11.生成器返回语句
function *createIterator() {
  yield 1;    
  return;
  yield 2;
  yield 3;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
function *createIterator() {
  yield 1;    
  return 42;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 42, done: true }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
12.委托生成器
function *createNumberIterator() {
  yield 1;
  yield 2;
}
function *createColorIterator() {
  yield "red";
  yield "green";
}
function *createCombinedIterator() {
  yield *createNumberIterator();
  yield *createColorIterator();
  yield true;
}
var iterator = createCombinedIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: "red", done: false }"
console.log(iterator.next()); // "{ value: "green", done: false }"
console.log(iterator.next()); // "{ value: true, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
function *createNumberIterator() {
  yield 1;
  yield 2;    
  return 3;
}
function *createRepeatingIterator(count) {    
  for (let i=0; i < count; i++) {
    yield "repeat";
  }
}
function *createCombinedIterator() {
  let result = yield *createNumberIterator();
  yield *createRepeatingIterator(result);
}
var iterator = createCombinedIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"
function *createNumberIterator() {
  yield 1;
  yield 2;    
  return 3;
}
function *createRepeatingIterator(count) {    
  for (let i=0; i < count; i++) {
    yield "repeat";
  }
}
function *createCombinedIterator() {
  let result = yield *createNumberIterator();
  yield result;
  yield *createRepeatingIterator(result);
}
var 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: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

[注意] yield*也可直接应用于字符串,例如yield* "hello",此时将使用字符串的默认迭代器

function *createStringIterator() {
  yield* "hello"
}
var iterator = createStringIterator();
console.log(iterator.next());  // "{ value: 'h', done: false }"
console.log(iterator.next());  // "{ value: 'e', done: false }"
console.log(iterator.next());  // "{ value: 'l', done: false }"
console.log(iterator.next());  // "{ value: 'l', done: false }"
console.log(iterator.next());  // "{ value: 'o', done: false }"
console.log(iterator.next());  // "{ value: undefined, done: true }" //
13.异步任务执行

生成器令人兴奋的特性多与异步编程有关,JS中的异步编程有利有弊:简单任务的异步化非常容易;而复杂任务的异步化会带来很多管理代码的挑战。由于生成器支持在函数中暂停代码执行,因而可以深入挖掘异步处理的更多用法

执行异步操作的传统方式一般是调用一个函数并执行相应回调函数

// 从磁盘读取文件
let fs = require("fs");
fs.readFile("config.json", function(err, contents) {    
  if (err) {
    throw err;
  }
  doSomethingWith(contents);
  console.log("Done");
});

【简单任务执行器】

由于执行yield语句会暂停当前函数的执行过程并等待下一次调用next()方法,因此可以创建一个函数,在函数中调用生成器生成相应的迭代器,从而在不用回调函数的基础上实现异步调用next()方法

function run(taskDef) {
  // 创建迭代器,让它在别处可用
  let task = taskDef();// 启动任务
  let result = task.next();// 递归使用函数来保持对 next() 的调用
  function step() {
    // 如果还有更多要做的
    if (!result.done) {
      result = task.next();
      step();
    }
  }
  // 开始处理过程 
  step();
}
run(function*() {
  console.log(1);
  yield;
  console.log(2);
  yield;
  console.log(3);
});

【向任务执行器传递数据】

给任务执行器传递数据的最简单办法是,将值通过迭代器的next()方法传入作为yield的生成值供下次调用。在这段代码中,只需将result.value传入next()方法即可

function run(taskDef) { // 创建迭代器,让它在别处可用
  let task = taskDef(); // 启动任务
  let result = task.next(); // 递归使用函数来保持对 next() 的调用
  function step() {
    // 如果还有更多要做的
    if (!result.done) {
      result = task.next(result.value);
        step();
      }
    }
    // 开始处理过程 
   step();
}
run(function*() {
 let value = yield 1;
 console.log(value); // 1
 value = yield value + 3;
 console.log(value); // 4
});

【异步任务执行器】

之前的示例只是在多个yield调用间来回传递静态数据,而等待一个异步过程有些不同。任务执行器需要知晓回调函数是什么以及如何使用它。由于yield表达式会将值返回给任务执行器,所有的函数调用都会返回一个值,因而在某种程度上这也是一个异步操作,任务执行器会一直等待直到操作完成

function fetchData() {
  return function(callback) {
    callback(null, "Hi!");
  };
}
function fetchData() {
  return function(callback) {
    setTimeout(function() {
      callback(null, "Hi!");
    }, 50);
  };
}
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 (err) {
            result = task.throw(err);                        
            return;
          }
          result = task.next(data);
          step();
        });
      } else {
        result = task.next(result.value);
        step();
      }
    }
  }
  // 开始处理过程 
  step();
}
let fs = require("fs");    
function readFile(filename) {
  return function(callback) {
    fs.readFile(filename, callback);
  };
}
run(function*() {
  let contents = yield readFile("config.json");
  doSomethingWith(contents);
  console.log("Done");
});
上一篇 下一篇

猜你喜欢

热点阅读