이터레이터란 반복 처리가 가능한 객체를 말한다.
반복 가능한 객체라고 하면 배열을 떠올릴 수 있지만 배열은 이터레이터가 아니다. 하지만 배열은 Symbol.iterator
메서드를 가지고 있는데, 이 메서드는 이터레이터를 반환한다.
var iter = a[Symbol.iterator]();
Symbol.iterator
메서드를 가진 객체. for/of 문으로 순회 가능하다.
이터레이터 ≠ 이터러블한 객체
이터레이터에는 next()
메서드가 있는데, 이를 호출할 때마다 이터레이터 리절트(iterator result)가 반환된다.
value
, done
프로퍼티를 갖는 객체console.log(iter.next()); // -> Object { value: 5, done: false }
value
: 꺼낸 값done
: 반복이 끝났는지 뜻하는 논리값for/of 문은 이터레이터를 반환하는 Symbol.iterator
메서드를 가지고 있는 객체를 반복 처리한다.
이터러블한 내장 객체의 이터레이터도 이터러블하다.
🤔 Map.prototype.keys(), values()
는 배열이 아닌 이터레이터 객체인데 어떻게 for/of 문으로 순회할 수 있을까?
→ 두 메서드의 결과값은 이터레이터 객체이지만 이터러블한 객체이기도 하다.
그 외 Array
, String
, TypedArray
, Map
, Set
으로 생성한 객체는 반복 처리를 할 수 있고, 이들 객체로 생성한 이터레이터도 반복 처리할 수 있다.
제네레이터는 이터레이터를 값으로 반환하는 함수로, 여러 개의 값을 필요에 따라 하나씩 반환(yield)함으로써 손쉽게 데이터 스트림을 만들기 위해 사용된다.
제네레이터는 function*
문으로 정의해서 생성할 수 있다.
function* generateSequence() {
yield 1;
yield 2;
return 3;
}
var iter = generateSequence();
next()
메서드를 호출하면 가장 가까운 yield <value>
문을 만날 때까지 실행된다. 실행의 결과로 value
와 함수의 코드 실행이 끝났는지 뜻하는 논리값인 done
을 가진 객체를 반환한다.console.log(iter.next()) // -> { value: 1, done: false }
next()
를 또 호출하면 이전에 실행이 끝난 위치에서 시작하여 처리를 재개하고, 다음 번 yield
연산자의 위치까지 실행한다.console.log(iter.next()) // -> { value: 1, done: false }
console.log(iter.next()) // -> { value: 2, done: false }
console.log(iter.next()) // -> { value: 3, done: true }
for (var v of iter) console.log(v); // 1, 2를 순서대로 표시한다.
❗ for/of 이터레이션은 done: true
일 때 마지막 value
를 무시한다. 모든 값이 출력되길 원한다면 return
이 아닌 yield
로 값을 반환해야 한다.next()
메서드에 값을 대입하면 제네레이터에 값을 넘길 수 있다. 넘긴 값은 일시적으로 정지하기 직전의 yield 값으로 사용된다.
function* fibonacci() {
var fn1 = 0, fn2 = 1;
while(true) {
var fnew = fn1 + fn2;
fn1 = fn2;
fn2 = fnew;
reset = yield fn1;
if (reset) {
fn1 = 0; fn2 = 1;
}
}
}
var iter = fibonacci();
for(var i = 0; i < 10; i++){
console.log(iter.next().value); // 1, 1, 2, 3, 5, ... 55가 순서대로 출력된다.
}
console.log(iter.next().value); // 89
console.log(iter.next(true).value); // 1 (리셋됨!!)
위 예시에서 next()
메서드가 실행되면 fn1 값을 반환하고 일시 정지하게 된다. 만약 아무런 값도 넘기지 않을 경우 reset의 값은 undefined
가 되므로 초기화가 진행되지 않고 이어서 실행된다. 반면 next(true)
와 같이 넘길 경우 이전 yield
값을 받는 reset
이 true
가 되면서 초기화가 진행된다.
yield*
를 사용해서 이터러블한 객체를 지정하면 여기서 순차적으로 값을 꺼내 각각의 값에 yield를 적용한다.
function* g(){
yield 0;
yield* [2, 4];
yield* "AB";
}
위 제네레이터의 반환값을 순회하면 0, 2, 4, A, B의 순서대로 출력된다.
참고자료
책 <모던 자바스크립트 입문> 이소 히로시 지음
Map.prototype.keys() - JavaScript | MDN
제너레이터