阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 可迭代对象

可迭代对象

作者:陈川 阅读数:37479人阅读 分类: JavaScript

可迭代协议

ECMAScript 6 引入了可迭代协议(Iterable Protocol),允许对象定义或定制它们的迭代行为。一个对象要成为可迭代对象,必须实现 @@iterator 方法,即对象或其原型链上必须有一个键为 Symbol.iterator 的属性。

const iterableObject = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 5) {
          return { value: step, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

内置可迭代对象

ES6 中许多内置对象默认就是可迭代的,包括:

  • Array
  • String
  • Map
  • Set
  • TypedArray
  • arguments 对象
  • DOM NodeList
// 数组是可迭代的
const arr = [1, 2, 3];
for (const item of arr) {
  console.log(item);
}

// 字符串也是可迭代的
const str = 'hello';
for (const char of str) {
  console.log(char);
}

迭代器协议

迭代器协议定义了产生一系列值的标准方式。一个对象要成为迭代器,必须实现 next() 方法,该方法返回一个包含两个属性的对象:

  • value:迭代器返回的值
  • done:布尔值,表示迭代是否完成
function createIterator(array) {
  let index = 0;
  return {
    next() {
      return index < array.length 
        ? { value: array[index++], done: false }
        : { value: undefined, done: true };
    }
  };
}

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 }

自定义可迭代对象

我们可以创建自定义的可迭代对象,只需要实现 Symbol.iterator 方法:

class Range {
  constructor(start, end, step = 1) {
    this.start = start;
    this.end = end;
    this.step = step;
  }

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

const range = new Range(1, 5);
for (const num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

可迭代对象的应用场景

可迭代对象在ES6中有多种应用场景:

  1. for...of循环:专门用于遍历可迭代对象
  2. 展开运算符:可以将可迭代对象展开为单独的元素
  3. 解构赋值:可以从可迭代对象中提取值
  4. Array.from():将可迭代对象转换为数组
  5. Map、Set、WeakMap、WeakSet构造函数:接受可迭代对象作为参数
// for...of
const set = new Set([1, 2, 3]);
for (const item of set) {
  console.log(item);
}

// 展开运算符
const arr = [...'hello']; // ['h', 'e', 'l', 'l', 'o']

// 解构赋值
const [first, second] = new Set([1, 2, 3]);
console.log(first, second); // 1 2

// Array.from
const map = new Map([[1, 'one'], [2, 'two']]);
const arrayFromMap = Array.from(map); // [[1, 'one'], [2, 'two']]

生成器与可迭代对象

生成器函数返回的生成器对象既是迭代器又是可迭代对象,这使得创建可迭代对象更加简洁:

function* generateSequence(start, end) {
  for (let i = start; i <= end; i++) {
    yield i;
  }
}

const sequence = generateSequence(1, 5);
for (const num of sequence) {
  console.log(num); // 1, 2, 3, 4, 5
}

// 生成器对象既是迭代器又是可迭代对象
const anotherSequence = generateSequence(1, 3);
console.log(anotherSequence[Symbol.iterator]() === anotherSequence); // true

异步可迭代对象

ES2018 引入了异步迭代器和异步可迭代对象,用于处理异步数据流:

async function* asyncGenerateSequence(start, end) {
  for (let i = start; i <= end; i++) {
    // 模拟异步操作
    await new Promise(resolve => setTimeout(resolve, 100));
    yield i;
  }
}

(async () => {
  const asyncSequence = asyncGenerateSequence(1, 3);
  for await (const num of asyncSequence) {
    console.log(num); // 1 (100ms后), 2 (200ms后), 3 (300ms后)
  }
})();

可迭代对象的组合

可迭代对象可以组合使用,创建更复杂的数据处理管道:

function* take(iterable, count) {
  let taken = 0;
  for (const item of iterable) {
    if (taken >= count) return;
    yield item;
    taken++;
  }
}

function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item;
    }
  }
}

function* map(iterable, mapper) {
  for (const item of iterable) {
    yield mapper(item);
  }
}

const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const result = take(
  map(
    filter(numbers, n => n % 2 === 0),
    n => n * 2
  ),
  3
);

console.log([...result]); // [4, 8, 12]

可迭代对象的性能考虑

在处理大型数据集时,可迭代对象比数组更节省内存,因为它们可以按需生成值,而不是一次性生成所有值:

function* generateLargeDataset() {
  for (let i = 0; i < 1e6; i++) {
    yield i;
  }
}

// 内存高效,因为值是按需生成的
for (const num of generateLargeDataset()) {
  if (num > 10) break;
  console.log(num);
}

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益,请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:迭代协议基本概念

下一篇:迭代器对象

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌