阿里云主机折上折
  • 微信号
Current Site:Index > Iterable object

Iterable object

Author:Chuan Chen 阅读数:37478人阅读 分类: JavaScript

The Iterable Protocol

ECMAScript 6 introduced the Iterable Protocol, which allows objects to define or customize their iteration behavior. For an object to be iterable, it must implement the @@iterator method, meaning the object or its prototype chain must have a property with the key 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 };
      }
    };
  }
};

Built-in Iterable Objects

Many built-in objects in ES6 are iterable by default, including:

  • Array
  • String
  • Map
  • Set
  • TypedArray
  • The arguments object
  • DOM NodeList
// Arrays are iterable
const arr = [1, 2, 3];
for (const item of arr) {
  console.log(item);
}

// Strings are also iterable
const str = 'hello';
for (const char of str) {
  console.log(char);
}

The Iterator Protocol

The Iterator Protocol defines a standard way to produce a sequence of values. For an object to be an iterator, it must implement a next() method, which returns an object with two properties:

  • value: The value returned by the iterator
  • done: A boolean indicating whether the iteration is complete
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 }

Custom Iterable Objects

We can create custom iterable objects by implementing the Symbol.iterator method:

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
}

Use Cases for Iterable Objects

Iterable objects have various applications in ES6:

  1. for...of loops: Specifically designed to iterate over iterable objects
  2. Spread operator: Can expand an iterable object into individual elements
  3. Destructuring assignment: Can extract values from an iterable object
  4. Array.from(): Converts an iterable object into an array
  5. Map, Set, WeakMap, WeakSet constructors: Accept iterable objects as arguments
// for...of
const set = new Set([1, 2, 3]);
for (const item of set) {
  console.log(item);
}

// Spread operator
const arr = [...'hello']; // ['h', 'e', 'l', 'l', 'o']

// Destructuring assignment
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']]

Generators and Iterable Objects

Generator functions return generator objects that are both iterators and iterable, making it more concise to create iterable objects:

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
}

// Generator objects are both iterators and iterable
const anotherSequence = generateSequence(1, 3);
console.log(anotherSequence[Symbol.iterator]() === anotherSequence); // true

Asynchronous Iterable Objects

ES2018 introduced asynchronous iterators and asynchronous iterable objects for handling asynchronous data streams:

async function* asyncGenerateSequence(start, end) {
  for (let i = start; i <= end; i++) {
    // Simulate an asynchronous operation
    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 (after 100ms), 2 (after 200ms), 3 (after 300ms)
  }
})();

Combining Iterable Objects

Iterable objects can be combined to create more complex data processing pipelines:

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]

Performance Considerations for Iterable Objects

When working with large datasets, iterable objects are more memory-efficient than arrays because they generate values on demand rather than all at once:

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

// Memory-efficient because values are generated on demand
for (const num of generateLargeDataset()) {
  if (num > 10) break;
  console.log(num);
}

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

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

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

下一篇:迭代器对象

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.