이터레이션 프로토콜
이터레이션 프로토콜은 자바스크립트에서 컬렉션(예: 배열, 문자열, 맵, 세트 등)의 요소를 순회하는 방법을 정의하는 규약입니다. 이 프로토콜은 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 키워드를 사용하여 연속된 값을 생성할 수 있습니다. 제너레이터는 내부적으로 Iterable 및 Iterator 프로토콜을 모두 구현하므로, 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의 다양한 내장 기능과 잘 통합되어 강력한 데이터 순회 및 조작 기능을 구현할 수 있습니다.