自定义迭代器和生成器

概念

迭代器

  • 定义 :迭代器是实现了 next() 方法的对象,每次调用返回 { value: any, done: boolean },用于按需遍历数据集合。value 代表当前迭代的值,done 是一个布尔值,true 表示迭代结束,false 表示还有更多值可迭代。
  • 可迭代对象 :任何实现了 Symbol.iterator 方法的对象均可被 for...of 遍历(如数组、Set、Map)。
  • 核心方法
scss 复制代码
const iterator = {
  next() { /* 返回下一个值 */ },
  return() { /* 提前终止迭代 */ },
  throw() { /* 抛出异常 */ }
};

生成器

  • 定义 :生成器是使用 function* 定义的函数,通过 yield 暂停执行并按需生成值,返回一个符合迭代器协议的对象。
  • 特性
    • 支持双向通信(通过 next(value) 传递参数)。
    • 天然支持异步操作(结合 yield 和 Promise)。
javascript 复制代码
function* myGenerator() {
  yield 1;
  yield 2;
}

Symbol.iterator

Symbol.iterator 是 JavaScript 中的一个内置符号(全局的、独一无二的)。

它主要用于定义对象的默认迭代器,使得对象能借助 for...of 循环等迭代机制进行遍历。

当一个对象实现了以 Symbol.iterator 为键的方法时,这个对象就被称为 "可迭代对象",该方法会返回一个迭代器对象。

自定义实现以及使用场景

迭代器

基础实现

1. 通过类实现迭代器

定义一个类并实现 [Symbol.iterator]() 方法,返回包含 next() 方法的迭代器对象:

kotlin 复制代码
class NumberRange {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() {
    let current = this.start;
    return {
      next: () => {
        return current <= this.end 
          ? { value: current++, done: false } 
          : { done: true };
      }
    };
  }
}

核心逻辑

  • next() 每次返回 { value, done } 对象,donetrue 时终止迭代;
  • 示例:遍历 new NumberRange(1, 5) 会输出 1 到 5。
2. 通过普通对象实现

为普通对象添加 Symbol.iterator 方法,支持 for...of 遍历:

ini 复制代码
const colors = { blue: "蓝色", green: "绿色", yellow: "黄色" };

colors[Symbol.iterator] = function() {
  const keys = Object.keys(this);
  let index = 0;
  return {
    next: () => {
      return index < keys.length 
        ? { value: this[keys[index++]], done: false } 
        : { done: true };
    }
  };
};
  • 应用场景 :遍历对象键值对,如 for (const val of colors) 输出颜色值。

高级实现技巧

1. 生成器函数简化实现

使用 function* 生成器自动管理迭代状态,无需手动维护 next()

vbnet 复制代码
const obj = { a: 1, b: 2 };

obj[Symbol.iterator] = function* () {
  for (const key of Object.keys(this)) {
    yield { key, value: this[key] };
  }
};
  • 优势 :代码更简洁,自动处理 valuedone 状态。
2. 支持提前终止

为迭代器添加 return() 方法,处理 break 或错误中断:

javascript 复制代码
class SafeRange {
  [Symbol.iterator]() {
    let current = this.start;
    return {
      next() { /* ... */ },
      return() {
        console.log('迭代提前终止');
        return { done: true };
      }
    };
  }
}

触发条件 :当循环被 breakreturn 或抛出错误时调用。

应用场景

1. 自定义数据结构迭代

实现双向迭代器(如链表或树结构):

kotlin 复制代码
class BidirectionalRange {
  constructor(start, end) {
    this.current = start;
    this.start = start;
    this.end = end;
  }

  [Symbol.iterator]() { return this; }

  next() {
    return this.current <= this.end 
      ? { value: this.current++, done: false } 
      : { done: true };
  }

  prev() {
    return this.current > this.start 
      ? { value: --this.current, done: false } 
      : { done: true };
  }
}
  • 用途:支持正向和逆向遍历。
2. 惰性求值与无限序列

生成无限斐波那契数列:

ini 复制代码
function* fibonacci() {
  let a = 0, b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

特点:按需生成值,无需预加载全部数据。

生成器

基础实现

1. 生成器函数定义

生成器函数通过 function* 声明,并通过 yield 关键字暂停执行:

javascript 复制代码
function* foo(index) {
  while (index < 2) {
    yield index;
    index++;
  }
}

const iterator = foo(0);

console.log(iterator.next().value);
// 0

console.log(iterator.next().value);
//  1
2.自定义迭代器

通过 Symbol.iterator 将生成器绑定到对象,使其可被 for...of 遍历:

vbnet 复制代码
const colors = { blue: "蓝色", green: "绿色" };

colors[Symbol.iterator] = function* () {
  for (const key of Object.keys(this)) {
    yield { key, value: this[key] }; // 返回键值对
  }
};

// 遍历结果:{ key: 'blue', value: '蓝色' }, { key: 'green', value: '绿色' }

高级应用技巧

1. 双向通信

通过 next() 向生成器传递参数,动态修改执行流程:

javascript 复制代码
function* dialog() {
  const name = yield "你的名字?";
  const hobby = yield "你的爱好?";
  return `${name}喜欢${hobby}`;
}

const chat = dialog();
console.log(chat.next().value);        // "你的名字?"
console.log(chat.next("小明").value);  // "你的爱好?"
console.log(chat.next("编程").value);  // "小明喜欢编程"

应用场景:用户输入交互、动态配置。

2. 惰性求值与无限序列

生成器按需生成数据,节省内存:

ini 复制代码
function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 0, 1, 1, 2, 3, 5...
3. 异步流程控制

结合 Promise 实现同步风格的异步代码:

scss 复制代码
function* asyncTask() {
  const data = yield fetchData();
  const processed = yield processData(data);
  return saveData(processed);
}

// 自动执行器
function run(generator) {
  const gen = generator();
  function handle(result) {
    if (result.done) return result.value;
    return result.value.then(data => handle(gen.next(data)));
  }
  return handle(gen.next());
}

应用场景

1. 分页数据加载

按需逐页获取 API 数据:

ini 复制代码
function* fetchPaginatedData(apiUrl) {
  let page = 1;
  while (true) {
    const response = yield fetch(`${apiUrl}?page=${page}`);
    const data = yield response.json();
    if (data.length === 0) break;
    yield data;
    page++;
  }
}
  • 适用场景:大数据分页、动态加载。
2. 状态机

实现循环状态切换(如交通灯):

javascript 复制代码
function* trafficLight() {
  while (true) {
    yield 'Green';
    yield 'Yellow';
    yield 'Red';
  }
}
3. 文件分段读取

逐块处理大文件,避免内存溢出:

ini 复制代码
const fs = require('fs');
function* readFileByChunks(filePath, chunkSize) {
  const buffer = Buffer.alloc(chunkSize);
  let position = 0;
  const fd = fs.openSync(filePath, 'r');
  while (true) {
    const bytesRead = fs.readSync(fd, buffer, 0, chunkSize, position);
    if (bytesRead === 0) break;
    yield buffer.slice(0, bytesRead);
    position += bytesRead;
  }
  fs.closeSync(fd);
}
相关推荐
@PHARAOH1 小时前
HOW - 在 Mac 上的 Chrome 浏览器中调试 Windows 场景下的前端页面
前端·chrome·macos
独行soc2 小时前
2025年渗透测试面试题总结-某服面试经验分享(附回答)(题目+回答)
linux·运维·服务器·网络安全·面试·职场和发展·渗透测试
月月大王3 小时前
easyexcel导出动态写入标题和数据
java·服务器·前端
JC_You_Know4 小时前
多语言网站的 UX 陷阱与国际化实践陷阱清单
前端·ux
Python智慧行囊4 小时前
前端三大件---CSS
前端·css
Jinuss5 小时前
源码分析之Leaflet中Marker
前端·leaflet
成都渲染101云渲染66665 小时前
blender云渲染指南2025版
前端·javascript·网络·blender·maya
聆听+自律5 小时前
css实现渐变色圆角边框,背景色自定义
前端·javascript·css
行走__Wz5 小时前
计算机学习路线与编程语言选择(信息差)
java·开发语言·javascript·学习·编程语言选择·计算机学习路线
-代号95275 小时前
【JavaScript】二十九、垃圾回收 + 闭包 + 变量提升
开发语言·javascript·ecmascript