🔄

How to Create Custom Iterators

JavaScriptAdvanced

Make objects iterable with custom iterators.

Code Example

JavaScript
// Basic iterator
const iterable = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step <= 3) {
          return { value: step, done: false };
        }
        return { done: true };
      }
    };
  }
};

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

// Custom range 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 (current <= end) {
          const value = current;
          current += step;
          return { value, done: false };
        }
        return { done: true };
      }
    };
  }
}

const range = new Range(1, 10, 2);
console.log([...range]); // [1, 3, 5, 7, 9]

// Iterable object
class LinkedList {
  constructor() {
    this.head = null;
  }
  
  add(value) {
    const node = { value, next: null };
    if (!this.head) {
      this.head = node;
    } else {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = node;
    }
  }
  
  [Symbol.iterator]() {
    let current = this.head;
    return {
      next() {
        if (current) {
          const value = current.value;
          current = current.next;
          return { value, done: false };
        }
        return { done: true };
      }
    };
  }
}

const list = new LinkedList();
list.add(1);
list.add(2);
list.add(3);

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

// Bidirectional iterator
class Counter {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }
  
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    let direction = 1;
    
    return {
      next() {
        if (current <= end && direction === 1) {
          const value = current;
          current++;
          return { value, done: false };
        }
        return { done: true };
      },
      return() {
        console.log("Iterator cleaned up");
        return { done: true };
      }
    };
  }
}

const counter = new Counter(1, 3);
for (const val of counter) {
  console.log(val);
}

// Spread operator works with iterables
const arr = [...new Range(1, 5)];
console.log(arr); // [1, 2, 3, 4, 5]

// Destructuring works too
const [first, second, ...rest] = new Range(1, 5);
console.log(first, second, rest);

Explanation

Implement [Symbol.iterator]() to make objects iterable. Return object with next() method. next() returns {value, done}. Works with for...of, spread, destructuring.

Related Tutorials