[JS] Iterator Helper

장동균·2025년 4월 21일
0
let iter = (1..)
    .filter(|x| x % 2 == 0)
    .take(5); // 여기선 아직 실행되지 않음

위 코드는 Rust Iterator 예제이다. Iterator 연산은 기본적으로 지연 평가(Lazy Evaluation)의 대상이다. collect(), sum()과 같은 consumer가 호출되기 전까지 실제로 실행되지 않는다.

Iterator - JavaScript | MDN
자바스크립트에서는 이와 같은 기능들이 ES17(ECMAScript 2026)에서 추가되었다.


새로운 기능을 확인하기 이전에 이터러블과 이터레이터에 대한 정의를 확인할 필요가 있다.

📦 이터러블 (Iterable)
• for...of 루프나 전개 연산자(...)에 사용할 수 있는 객체
• 필수 조건: Symbol.iterator 메서드가 있어야 함

🔁 이터레이터 (Iterator)
.next() 메서드가 있어서 반복할 수 있는 객체
{ value, done } 구조로 값을 반환

let range = {
  from: 1,
  to: 5
};

range[Symbol.iterator] = function() {
  return {
    current: this.from,
    last: this.to,

    next() {
      if (this.current <= this.last) {
        return { done: false, value: this.current++ };
      } else {
        return { done: true };
      }
    }
  };
};

for (let num of range) {
  alert(num); // 1, then 2, 3, 4, 5
}

예제 코드를 기준으로 range는 이터러블 객체 range[Symbol.iterator]()의 반환값이 이터레이터 객체라고 할 수 있다.

let range = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    this.current = this.from;
    return this;
  },

  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ };
    } else {
      return { done: true };
    }
  }
};

물론 range 자체를 이터레이터로 만들면 코드는 더 간단해진다.


일부 Array의 헬퍼 메서드들이 Iterator의 헬퍼 메서드들과 1대1 매칭이 된다. filter 메서드를 기준으로 둘에는 어떠한 차이가 존재하는지 확인한다.

항목Array.prototype.filterIterator.prototype.filter
대상배열이터러블/이터레이터
평가 시점즉시 실행지연 실행
반환값새로운 배열새 이터레이터
사용 방식일반 배열에서 즉시 결과 추출필요한 만큼만 값을 계산
메모리 효율낮을 수 있음 (전체 배열 생성)높음 (필요한 만큼만 계산)
const arr = [1, 2, 3, 4, 5];
const even = arr.filter(x => x % 2 === 0);

console.log(even); // [2, 4]
  • 전체 배열을 즉시 순회하고
  • 조건에 맞는 요소만 모아서 새로운 배열을 반환한다.
  • 한번에 전부 처리하기 때문에, 큰 배열일 경우 메모리와 성능에 부담을 줄 수 있다.
const iter = [1, 2, 3, 4, 5].values(); // 이터레이터 생성
const filtered = iter.filter(x => x % 2 === 0);

console.log(filtered.next()); // { value: 2, done: false }
console.log(filtered.next()); // { value: 4, done: false }
console.log(filtered.next()); // { value: undefined, done: true }
  • 지연 평가: .next()를 호출할 때마다 조건을 확인하고 값을 계산한다.
  • 필터링된 이터레이터를 반환한다. 즉, 그 자체가 순회 사능한 객체이다.

두 방식을 의사 코드로 구현해본다.

ECMAScript 2024 spec: Array.prototype.filter

Array.prototype.filter = function(callback, thisArg) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    if (i in this) {
      const val = this[i];
      if (callback.call(thisArg, val, i, this)) {
        result.push(val);
      }
    }
  }
  return result;
};

Iterator Helpers

Iterator.prototype.filter = function (predicate) {
  // this는 iterator이어야 한다
  const source = this;
  return {
    next() {
      while (true) {
        const { value, done } = source.next();
        if (done) return { value: undefined, done: true };
        if (predicate(value)) return { value, done: false };
      }
    },
    [Symbol.iterator]() {
      return this;
    }
  };
};

javascript values vs array method
해당 링크의 테스트 결과를 시각화해서 확인한다.
배열의 크기가 작은 경우 큰 차이가 존재하지 않지만, 배열의 크기가 커질 수록 ops의 차이가 극단적으로 커지는 것을 확인할 수 있다.
큰 배열일 수록 지연평가를 활용했을 때 얻을 수 있는 이점이 많다.


참고문헌

iterable 객체
proposal-iterator-helpers와 지연평가
2025년에 자바스크립트 개발자라면 꼭 알아야 할 기능들 | GeekNews
Rust Iterators in JavaScript. Generators in JavaScript | by Andrew Burkus | Medium
[ JS ] 이터레이터, 제너레이터, 지연평가 ( Iterator, Generator, Lazy Evaluation )
Comparing: array method chaining, generator delegations, and transducing · GitHub

profile
프론트 개발자가 되고 싶어요

1개의 댓글

comment-user-thumbnail
2025년 6월 17일

헬퍼

답글 달기