js中生成器和迭代器

迭代器

可迭代对象的定义

  1. 实现[Symbol.iterator]方法:该方法必须返回一个迭代器对象。

  2. 迭代器对象 :必须有一个next()方法,该方法返回一个对象,包含:

    • value:当前迭代的值,可以是任意类型。
    • done:布尔值,表示迭代是否已经结束。

示例

下面是一个简单的可迭代对象示例:

javascript

javascript 复制代码
const myIterable = {
  [Symbol.iterator]: function() {
    let count = 0;
    return {
      next: function() {
        if (count < 3) {
          count++;
          return { value: count, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

// 使用 for...of 循环遍历
for (const value of myIterable) {
  console.log(value); // 输出 1, 2, 3
}

内置可迭代对象

JavaScript中许多内置对象是可迭代的,例如:

  • Array
  • String
  • Map
  • Set
  • arguments 对象
  • NodeList 等DOM集合

可迭代对象的用途

  1. 用于for...of循环:可以直接遍历可迭代对象。
  2. 展开语法(Spread syntax) :例如 [...array]
  3. 解构赋值(Destructuring assignment) :例如 let [a, b] = iterable
  4. Array.from() :可以将可迭代对象转换为数组。
  5. Promise.all()Promise.race() 等接受可迭代对象的方法。
  6. MapSet 的构造函数

和生成器关系

生成器是迭代器语法糖;生成器函数返回的对象即是一个生成器,也是一个迭代器,因为他实现了迭代器协议

生成器

核心定义

生成器(Generator) 是 ES6 引入的一种特殊函数,它可以通过 yield 关键字暂停和恢复 函数的执行。生成器函数返回一个生成器对象 ,这个对象同时符合可迭代协议迭代器协议

语法特征

javascript

javascript 复制代码
// 生成器函数的定义
function* generatorFunction() {
    // 函数体
    yield 'value1';
    yield 'value2';
    return 'final value';
}

// 或者使用函数表达式
const generator = function* () {
    yield 'value';
};

生成器的基本使用

创建和执行生成器

javascript

lua 复制代码
// 1. 定义生成器函数
function* simpleGenerator() {
    console.log('开始执行');
    yield '第一个值';
    console.log('恢复执行');
    yield '第二个值';
    console.log('结束执行');
    return '最终返回值';
}

// 2. 创建生成器对象
const gen = simpleGenerator();
console.log('生成器创建完成,但尚未执行');

// 3. 执行生成器
console.log(gen.next()); 
// 输出: "开始执行"
// 输出: { value: '第一个值', done: false }

console.log(gen.next());
// 输出: "恢复执行"  
// 输出: { value: '第二个值', done: false }

console.log(gen.next());
// 输出: "结束执行"
// 输出: { value: '最终返回值', done: true }

console.log(gen.next());
// 输出: { value: undefined, done: true }

yield 关键字详解

javascript

lua 复制代码
function* yieldExamples() {
    // 1. 基本 yield
    yield '普通值';
    
    // 2. yield 表达式
    const result = yield '表达式结果';
    console.log('从外部传入的值:', result);
    
    // 3. yield 复杂表达式
    yield 2 + 3 * 4; // 14
    
    // 4. yield 函数调用
    yield Math.random();
    
    return '完成';
}

const exampleGen = yieldExamples();
console.log(exampleGen.next());    // { value: '普通值', done: false }
console.log(exampleGen.next());    // { value: '表达式结果', done: false }
console.log(exampleGen.next('外部传入的值')); // 从外部传入的值: 外部传入的值
                                              // { value: 14, done: false }
console.log(exampleGen.next());    // { value: 0.123..., done: false }
console.log(exampleGen.next());    // { value: '完成', done: true }

生成器的核心特性

1. 暂停和恢复执行

javascript

javascript 复制代码
function* pauseResumeDemo() {
    console.log('阶段 1');
    yield '暂停点 1';
    
    console.log('阶段 2');
    yield '暂停点 2';
    
    console.log('阶段 3');
    
    // 可以在循环中使用 yield
    for (let i = 0; i < 3; i++) {
        console.log(`循环第 ${i + 1} 次`);
        yield `循环值 ${i}`;
    }
    
    return '全部完成';
}

const demo = pauseResumeDemo();

// 分步执行,完全控制执行流程
let result = demo.next(); // "阶段 1"
console.log('手动处理其他任务...');
setTimeout(() => {
    result = demo.next(); // "阶段 2"
    console.log('继续其他任务...');
    
    setTimeout(() => {
        // 一次性执行剩余部分
        while (!result.done) {
            result = demo.next();
            console.log(result);
        }
    }, 1000);
}, 1000);

2. 双向通信

javascript

javascript 复制代码
function* twoWayCommunication() {
    console.log('生成器启动');
    
    // 第一次暂停,等待外部输入名字
    const name = yield '请问你的名字?';
    console.log(`收到名字: ${name}`);
    
    // 第二次暂停,等待外部输入年龄
    const age = yield `你好 ${name},请问你的年龄?`;
    console.log(`收到年龄: ${age}`);
    
    // 根据年龄判断
    if (age < 18) {
        return `${name},你还未成年`;
    } else {
        return `${name},你已经成年了`;
    }
}

// 使用生成器进行对话
const conversation = twoWayCommunication();

// 启动对话
let response = conversation.next();
console.log('系统:', response.value); // "请问你的名字?"

// 用户输入名字
response = conversation.next('Alice');
console.log('系统:', response.value); // "你好 Alice,请问你的年龄?"

// 用户输入年龄
response = conversation.next(25);
console.log('系统:', response.value); // "Alice,你已经成年了"

3. 错误处理

javascript

javascript 复制代码
function* errorHandlingGenerator() {
    try {
        console.log('开始执行');
        yield '第一步';
        
        // 这里可能会抛出错误
        const riskyValue = yield '进行危险操作';
        
        if (riskyValue === 'error') {
            throw new Error('手动触发的错误');
        }
        
        yield `成功: ${riskyValue}`;
        return '完成';
        
    } catch (error) {
        console.log('捕获到错误:', error.message);
        yield `错误处理: ${error.message}`;
        return '错误恢复完成';
    }
}

const errorGen = errorHandlingGenerator();

// 正常流程
console.log(errorGen.next());    // { value: '第一步', done: false }
console.log(errorGen.next('safe')); // { value: '成功: safe', done: false }
console.log(errorGen.next());    // { value: '完成', done: true }

// 错误流程
const errorGen2 = errorHandlingGenerator();
console.log(errorGen2.next());    // { value: '第一步', done: false }

// 从外部抛出错误到生成器内部
console.log(errorGen2.throw(new Error('外部错误')));
// 输出: "捕获到错误: 外部错误"
// 返回: { value: '错误处理: 外部错误', done: false }

console.log(errorGen2.next());    // { value: '错误恢复完成', done: true }

4、状态机

js 复制代码
function* trafficLight() {
    const states = ['红色', '黄色', '绿色'];
    let index = 0;
    
    while (true) {
        const command = yield states[index];
        
        if (command === 'next') {
            index = (index + 1) % states.length;
        } else if (command === 'reset') {
            index = 0;
        }
    }
}

// 使用交通灯状态机
const light = trafficLight();
console.log(light.next().value); // '红色'
console.log(light.next('next').value); // '黄色'
console.log(light.next('next').value); // '绿色'
console.log(light.next('next').value); // '红色'
console.log(light.next('reset').value); // '红色'
相关推荐
无我Code6 分钟前
前端-2025年末个人总结
前端·年终总结
文刀竹肃23 分钟前
DVWA -SQL Injection-通关教程-完结
前端·数据库·sql·安全·网络安全·oracle
LYFlied28 分钟前
【每日算法】LeetCode 84. 柱状图中最大的矩形
前端·算法·leetcode·面试·职场和发展
Bigger30 分钟前
Tauri(21)——窗口缩放后的”失焦惊魂”,游戏控制权丢失了
前端·macos·app
Bigger1 小时前
Tauri (20)——为什么 NSPanel 窗口不能用官方 API 全屏?
前端·macos·app
bug总结1 小时前
前端开发中为什么要使用 URL().origin 提取接口根地址
开发语言·前端·javascript·vue.js·html
一招定胜负2 小时前
网络爬虫(第三部)
前端·javascript·爬虫
Shaneyxs2 小时前
从 0 到 1 实现CloudBase云开发 + 低代码全栈开发活动管理小程序(13)
前端
半山烟雨半山青2 小时前
微信内容emoji表情包编辑器 + vue3 + ts + WrchatEmogi Editor
前端·javascript·vue.js
码途潇潇2 小时前
Vue 事件机制全面解析:原生事件、自定义事件与 DOM 冒泡完全讲透
前端·javascript