이터러블과 이터레이터, 제너레이터
이터레이션 프로토콜
ES6에서 도입된 이터레이션 프로토콜은 순회 가능한 데이터 컬렉션(자료구조)을 만들기 위해 ECMAScript 사양에 정의하여 미리 약속한 규칙이다.
순회 가능한 데이터 컬렉션을 이터레이션 프로토콜을 준수하는 이터러블로 통일하여 for…of 문, 스프레드 문법, 배열 디스트럭처링 할당의 대상으로 사용할 수 있도록 일원화했다.
이터러블
이터러블 프로토콜을 준수한 순회 가능한 자료 구조
Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체
Array, String, Map, Set 등
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const array = [1, 2, 3];
// 배열은 Array.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in array); // true
// 이터러블인 배열은 for...of 문으로 순회 가능하다.
for (const item of array) {
console.log(item);
}
// 이터러블인 배열은 스프레드 문법의 대상으로 사용할 수 있다.
console.log([...array]); // [1, 2, 3];
// 이터러블인 배열은 배열 디스트럭처럼 할당의 대상으로 사용할 수 있다.
const [a, ...rest] = array;
console.log(a, rest); // 1, [2, 3]
이터레이터
이터러블의 Symbol.iterator 메서드를 호출하면 이터레이터를 반환한다.
이터레이터는 next 메서드를 갖는다.
next 메서드를 호출하면 이터러블을 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체를 반환한다.
이터레이터 리절트 객체는 value와 done 프로퍼티를 갖는 객체다.
1
2
3
4
5
6
7
8
9
10
11
const array = [1, 2, 3];
// Symbol.iterator 메서드는 이터레이터를 반환한다. 이터레이터는 next 메서드를 갖는다.
const iterator = array[Symbol.iterator]();
// next 메서드를 호출하면 이터러블을 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체를 반환한다.
// 이터레이터 리절트 객체는 value와 done 프로퍼티를 갖는 객체다.
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
for…of 문
내부적으로 이터레이터의 next 메서드를 호출하여 이터러블을 순회하며 next 메서드가 반환한 이터레이터 리절트 객체의 value 프로퍼티 값을 for…of 변수에 할당한다.
이터레이터 리절트 객체의 done 프로퍼티 값이 false이면 이터러블의 순회를 계속하고 true이면 중단한다.
for…in문과의 차이점
- for…in문은 객체의 모든 프로퍼티 중에서 프로퍼티 어트리뷰트
[[Enumerable]]
의 값이 true인 프로퍼티를 순회하며 열거한다. 이 때 프로퍼티 키가 심벌인 프로퍼티는 열거하지 않는다.
- for…in문은 객체의 모든 프로퍼티 중에서 프로퍼티 어트리뷰트
1
2
3
4
for (const item of [1, 2, 3]) {
// item 변수에 순차적으로 1, 2, 3이 할당된다.
console.log(item); // 1, 2, 3
}
for문에서는 변수 선언문에 보통 let을 사용하지만, for…of 문에서는 const를 사용한다. 자세한 내용은 for문과 for…of문, let과 const에 정리
제너레이터
끝에 별표가 있는
function*
함수는 Generator 객체를 반환한다.Generator 함수를 호출하면 객체가 생성되는데, 이 객체는 프로토타입 체인을 통해 Symbol.iterator 속성을 갖고 있다.
1
2
3
4
5
6
7
8
9
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g[Symbol.iterator]; // ƒ [Symbol.iterator]() { [native code] }
- Generator는 이터러블, 이터레이터로 사용할 수 있다.
1
2
3
4
5
6
7
8
9
for (const i of g) {
console.log(i);
}
// 1
// 2
// 3
g.next();
// {value: undefined, done: true}