ES6/ES11知识点 续五

迭代器【Iterator】

ES6 中的**迭代器(Iterator)**是 JavaScript 的一种协议,它定义了对象如何被逐个访问。迭代器与 for...of、扩展运算符、解构赋值等语法密切相关。

📘 迭代器工作原理

ES6 迭代器的工作原理基于两个核心机制:

🌟 可迭代协议(Iterable Protocol)

如果一个对象实现了 Symbol.iterator 方法,并返回一个迭代器对象,那么它就是可迭代的(Iterable),可以用于:

  • for...of
  • 解构赋值(如 [a, b] = iterable)
  • ... 扩展运算符
  • Array.from()
javascript 复制代码
const iterable = {
  [Symbol.iterator]() {
    let i = 0;
    return {
      next() {
        return i < 3 ? { value: i++, done: false } : { done: true };
      }
    };
  }
};

for (const val of iterable) {
  console.log(val); // 0 1 2
}

⚙️迭代器协议(Iterator Protocol)

迭代器对象必须具有 next() 方法,每次调用返回一个对象:

javascript 复制代码
{ value: any, done: boolean }

含义:

  • value: 当前值
  • done: false: 迭代未结束
  • done: true: 迭代结束

当 done: true 时,value 可选,将被忽略

🔄 for...of 背后的流程

等价于下面这个展开流程:

javascript 复制代码
const iterator = iterable[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
  const value = result.value;
  // 执行代码块
  result = iterator.next();
}

📦 内置可迭代对象如何实现?

例如数组:

javascript 复制代码
const arr = [10, 20, 30];
const iter = arr[Symbol.iterator]();

iter.next(); // { value: 10, done: false }
iter.next(); // { value: 20, done: false }

浏览器底层实现了数组的 Symbol.iterator 方法,它会返回一个迭代器对象。

🧠 迭代器的本质理解

  • Symbol.iterator 是告诉 JavaScript:我可以被 for...of 遍历
  • 返回的迭代器对象 next() 方法控制了"值从哪里来"和"什么时候结束"
  • 你也可以用 yield(生成器)简化构建逻辑

🛠️ 手动创建一个迭代器

javascript 复制代码
function createIterator(arr) {
  let index = 0;
  return {
    next() {
      if (index < arr.length) {
        return { value: arr[index++], done: false };
      } else {
        return { value: undefined, done: true };
      }
    }
  };
}

const it = createIterator(['a', 'b', 'c']);
console.log(it.next()); // { value: 'a', done: false }
console.log(it.next()); // { value: 'b', done: false }
console.log(it.next()); // { value: 'c', done: false }
console.log(it.next()); // { value: undefined, done: true }

🚀 使对象可被 for...of 遍历

javascript 复制代码
const iterableObj = {
  data: [10, 20, 30],
  
  [Symbol.iterator]() {
    let i = 0;
    const data = this.data;
    return {
      next() {
        return i < data.length
          ? { value: data[i++], done: false }
          : { done: true };
      }
    };
  }
};

for (const val of iterableObj) {
  console.log(val);
}
// 输出:10 20 30

📦 内置可迭代对象

类型 可迭代? 示例

数组 ✅ for (let x of [1,2,3])

字符串 ✅ for (let c of 'abc')

Set / Map ✅ for (let e of new Set())

arguments ✅ for (let a of arguments)

DOM NodeList ✅ for (let el of nodelist)

普通对象 ❌ { a: 1 } ❌不能直接 for...of

🧙‍♂️ 配合生成器使用(语法糖)

javascript 复制代码
function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }

for (const x of gen()) {
  console.log(x); // 1, 2, 3
}

✅ 判断是否可迭代

javascript 复制代码
function isIterable(obj) {
  return typeof obj[Symbol.iterator] === 'function';
}

类数组对象添加自定义迭代器

方式一

javascript 复制代码
const arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3,

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

for (const val of arrayLike) {
  console.log(val); // 输出:a, b, c
}

方式二

使用生成器方式更优雅

javascript 复制代码
const arrayLike = {
  0: 'x',
  1: 'y',
  2: 'z',
  length: 3,

  *[Symbol.iterator]() {
    for (let i = 0; i < this.length; i++) {
      yield this[i];
    }
  }
};

for (const val of arrayLike) {
  console.log(val); // 输出:x, y, z
}

生成器

在 ES6 中,生成器(Generator)是一种特殊的函数,能控制函数的执行流程,支持按需产出(惰性求值),适合处理迭代、异步、状态机等场景。

🧱 基本语法

javascript 复制代码
function* myGenerator() {
  yield 1;
  yield 2;
  yield 3;
}
  • 使用 function* 定义生成器函数(注意星号 *)。
  • 内部使用 yield 表达式产生值。
  • 调用生成器函数返回的是一个迭代器对象。

🚀 调用与执行流程

javascript 复制代码
const gen = myGenerator();

console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
  • 每次调用 next(),函数执行到下一个 yield 停下来,返回一个 { value, done } 对象。
  • 最后 done: true 表示完成。

🔁 配合 for...of

javascript 复制代码
for (const val of myGenerator()) {
  console.log(val); // 输出:1, 2, 3
}

🧠 生成器的高级用法

✅ 接收外部值

javascript 复制代码
function* gen() {
  const a = yield 1;
  const b = yield a + 2;
  return b;
}

const it = gen();

console.log(it.next());       // { value: 1, done: false }
console.log(it.next(10));     // { value: 12, done: false }
console.log(it.next(100));    // { value: 100, done: true }
  • yield 表达式可以接收传入的值,作为上一个 yield 的返回值。

🛠 典型应用场景

生成无限序列

javascript 复制代码
function* idGenerator() {
  let id = 0;
  while (true) yield id++;
}

const ids = idGenerator();
console.log(ids.next().value); // 0
console.log(ids.next().value); // 1

自定义迭代器

javascript 复制代码
const obj = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  }
};

for (const x of obj) {
  console.log(x); // 输出:1, 2, 3
}

与异步协程结合(配合 async/await 或库如 co)

生成器可用于构造异步流程控制逻辑,尽管现在大多数场景被 async/await 替代。

yield* 的作用(委托子生成器)或生成器和 async/await 的区别

🔄 yield*:委托另一个生成器或可迭代对象

yield* 表达式可以将执行控制权交给另一个生成器或可迭代对象,就像"嵌套展开"。

✅ 示例 1:委托另一个生成器

javascript 复制代码
function* inner() {
  yield 'a';
  yield 'b';
}

function* outer() {
  yield 'start';
  yield* inner(); // 委托 inner 生成器
  yield 'end';
}

for (const val of outer()) {
  console.log(val); // 输出:start, a, b, end
}

✅ 示例 2:委托数组

javascript 复制代码
function* gen() {
  yield* [1, 2, 3];
}

console.log([...gen()]); // [1, 2, 3]

⚙️ 生成器 vs async/await

特性 生成器(Generator) async/await
关键字 function*, yield async function, await
控制流程方式 手动通过 .next() 推进 自动推进
异步支持 不直接支持(需配合库) 原生支持 Promise
返回值 迭代器对象 Promise
适合场景 同步流程控制、数据生成 异步流程控制

🔧 示例对比

1️⃣ 生成器实现异步流程(配合库如 co)

javascript 复制代码
function* asyncTask() {
  const data = yield fetch('...');
  console.log(data);
}
// 需要外部 runner 自动推进

2️⃣ async/await 简洁实现

javascript 复制代码
async function asyncTask() {
  const data = await fetch('...');
  console.log(data);
}

异步生成器 async function* 的基本用法

javascript 复制代码
async function* asyncGenerator() {
  let i = 0;
  while (i < 3) {
    await new Promise(resolve => setTimeout(resolve, 1000)); // 模拟异步操作
    yield i++;
  }
}

// 使用 for await...of 来消费异步生成器
(async () => {
  for await (const val of asyncGenerator()) {
    console.log(val); // 每秒输出一个数字:0, 1, 2
  }
})();

异步分页加载

javascript 复制代码
async function* fetchPages(total) {
  for (let page = 1; page <= total; page++) {
    const data = await fetch(`https://api.example.com/data?page=${page}`);
    const json = await data.json();
    yield json.items;
  }
}

(async () => {
  for await (const items of fetchPages(3)) {
    console.log('Page items:', items);
  }
})();

总结

  • yield* 是 生成器中的"合并子迭代器"工具。
  • async/await 是语法更清晰的异步生成器替代方案,自动推进,适合处理 Promise。
  • 生成器仍适用于同步状态控制、迭代器构造、无限序列、DSL 构建等。
相关推荐
Jiaberrr5 小时前
uniapp app 端获取陀螺仪数据的实现攻略
前端·javascript·vue.js·uni-app·陀螺仪
MINO吖5 小时前
项目改 pnpm 并使用 Monorepo 发布至 npm 上
前端·npm·node.js
几度泥的菜花6 小时前
Vue 项目中二维码生成功能全解析
javascript·vue.js·ecmascript
筱歌儿7 小时前
小程序问题(记录版)
前端·小程序
Jinuss8 小时前
源码分析之Leaflet中的LayerGroup
前端·leaflet
赶飞机偏偏下雨8 小时前
【前端笔记】CSS 选择器的常见用法
前端·css·笔记
LuckyLay9 小时前
AI教你学VUE——Deepseek版
前端·javascript·vue.js
我是哈哈hh9 小时前
【Vue】全局事件总线 & TodoList 事件总线
前端·javascript·vue.js·vue3·vue2
liuyang___9 小时前
vue3+ts的watch全解!
前端·javascript·vue.js