javascript/모던 자바스크립트 Deep Dive

이터레이션 프로토콜

yongfront 2024. 2. 21. 11:46
반응형
SMALL

이터레이션 프로토콜은 자바스크립트에서 컬렉션(예: 배열, 문자열, 맵, 세트 등)의 요소를 순회하는 방법을 정의하는 규약입니다. 이 프로토콜은 Iterable 프로토콜과 Iterator 프로토콜로 구성되어 있습니다.

Iterable 프로토콜

Iterable 프로토콜을 구현하는 객체는 for...of 루프와 같은 이터레이션 구문에서 사용될 수 있습니다. 이러한 객체는 Symbol.iterator 메서드를 구현해야 하며, 이 메서드는 **Iterator**를 반환해야 합니다.

let iterable = {
  [Symbol.iterator]() {
    let step = 0;
    return {
      next() {
        step++;
        if (step === 1) {
          return { value: 'Hello', done: false };
        } else if (step === 2) {
          return { value: 'World', done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (let value of iterable) {
  console.log(value); // "Hello" 다음 "World" 출력
}

Iterator 프로토콜

Iterator 프로토콜은 객체가 next 메서드를 가지고 있고, 이 메서드를 호출할 때마다 { value, done } 형태의 객체를 반환하는 것을 요구합니다. 여기서 **value**는 다음 값이고, **done**은 이터레이터가 끝에 도달했는지 여부를 나타내는 불리언 값입니다.

위의 예제에서 iterable 객체는 Iterable 프로토콜을 구현하고 있고, Symbol.iterator 메서드에 의해 반환된 객체는 Iterator 프로토콜을 구현하고 있습니다.

실용적인 이터레이션 프로토콜 예제: 무한 수열 생성기

이터레이션 프로토콜을 사용하여 무한 수열을 생성하는 제너레이터의 예를 살펴봅시다.

function* infiniteSequence() {
  let i = 0;
  while (true) {
    yield i++;
  }
}

let iterator = infiniteSequence();
console.log(iterator.next().value); // 0
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
// 계속해서 다음 값을 받아올 수 있음

이 예제에서 infiniteSequence 함수는 제너레이터 함수입니다. 제너레이터 함수는 호출될 때마다 **Iterator**를 반환하는 특별한 종류의 함수로, yield 키워드를 사용하여 연속된 값을 생성할 수 있습니다. 제너레이터는 내부적으로 IterableIterator 프로토콜을 모두 구현하므로, for...of 루프와 같은 이터레이션 구문에서 직접 사용할 수 있습니다.

이터레이션 프로토콜을 사용하면 복잡한 데이터 구조를 쉽게 순회하고, 사용자 정의 이터레이션 동작을 구현할 수 있어 자바스크립트 프로그래밍에서 매우 유용합니다.

 

 

이터레이터 예제

이터레이터의 간단한 예제로, 특정 범위의 숫자를 순회하는 이터레이터를 만들어 보겠습니다.

function makeRangeIterator(start = 0, end = Infinity, step = 1) {
    let nextIndex = start;
    let iterationCount = 0;

    const rangeIterator = {
       next: function() {
           let result;
           if (nextIndex < end) {
               result = { value: nextIndex, done: false };
               nextIndex += step;
               iterationCount++;
               return result;
           }
           return { value: iterationCount, done: true };
       }
    };
    return rangeIterator;
}

let it = makeRangeIterator(1, 10, 2);

console.log(it.next().value); // 1
console.log(it.next().value); // 3
console.log(it.next().value); // 5
console.log(it.next().value); // 7
console.log(it.next().value); // 9
console.log(it.next().done);  // true

Iterable 예제

이제 객체가 이터러블 프로토콜을 준수하게 만들어서, for...of 루프 등과 같은 JavaScript의 내장 반복 메커니즘을 사용할 수 있게 해보겠습니다.

const rangeIterable = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    // 이 객체는 iterator 객체를 반환해야 합니다.
    // next() 메서드를 가진 객체입니다.
    this.current = this.from;
    return {
      next: () => {
        if (this.current <= this.to) {
          return { value: this.current++, done: false };
        } else {
          return { done: true };
        }
      }
    };
  }
};

for (let value of rangeIterable) {
  console.log(value); // 1, 2, 3, 4, 5
}

이 예제에서 rangeIterable 객체는 이터러블 프로토콜을 구현합니다. Symbol.iterator 메서드는 이터레이터를 반환하고, 이 이터레이터는 **from**부터 **to**까지의 숫자를 순회할 수 있는 next 메서드를 제공합니다. 이를 통해 for...of 루프와 같은 반복 가능한 구문에서 사용할 수 있습니다.

이러한 방식으로 이터레이터와 이터러블 프로토콜을 사용하면 커스텀 객체를 반복 가능하게 만들 수 있으며, JavaScript의 다양한 내장 기능과 잘 통합되어 강력한 데이터 순회 및 조작 기능을 구현할 수 있습니다.

728x90
반응형
LIST