1. Array, Set, Map을 통해 알아보는 이터러블/이터레이터 프로토콜
- 이터러블 (Iterable): 이터레이터를 리턴하는 Symbol.iterator()를 가진 값
- 이터레이터 (iterator): {value, done} 객체를 리턴하는 next()를 가진 값
- 이터러블/이터레이블 프로토콜: 이터러블을 for.. of, 전개 연사자 등과 함께 동작하도록한 규약
1-1 예제 코드
const log = console.log;
log('Arr -----------');
const arr = [1, 2, 3];
let iter1 = arr[Symbol.iterator]();
log(iter1)
log(iter1.next())
for (const a of iter1) log(a);
for (const a of iter1) log(a);
const arr1 = [4, 5, 6];
arr1[Symbol.iterator] = null;
for (const a of arr1) log(a);
Array Iterator {}
{value: 1, done: false}
2
3
{value: undefined, done: true}
VM2897:1 Uncaught TypeError: arr is not iterable
at <anonymous>:1:17
- for .. of문은 iterable한 객체를 순회할 때 사용하는 문법이다. 즉, 인덱스를 이용한 순회가 아닌 Iterator를 이용해서 순회하는 방식이다.
- iterator에 있는 next 메서드의 역할은 다음 요소에 읽어 올 요소가 있는지 확인한다. Array의 마지막 요소까지 불러온 다음에 next 메서드 실행 시, 값을 정의한 value의 값은 undefined, 끝을 알려주는 done의 값은 true가 된다. 이 후 for문으로 arr을 순회하려 해도 값이 출력되지 않는다.
- arr[Symbol.iterator], 즉 arr의 이터레이터를 null로 할당한 뒤, for..of문 실행 시, iterable 하지 않다는 에러 로그를 출력한다.
const log = console.log;
log('Set -----------');
const set = new Set([1, 2, 3]);
let setIter = set[Symbol.iterator]();
log(setIter)
for (const a of set) log(a);
log('Map -----------');
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
for (const a of map.keys()) log(a);
for (const a of map.values()) log(a);
for (const a of map.entries()) log(a);
- Set, Map도 마찬가지로 Iterable 객체이다.
- Map 같은 경우 Key, values, entries 메서드 사용 시, Iterator를 반환한다. 그래서 위 코드와 같이 for.. of 문에서 사용이 가능하다.
2. 사용자 정의 이터러블을 통해 알아보기
2-1. 잘못된 이터러블
const iterable = {
[Symbol.iterator]() {
let i = 3;
return {
next() {
return i == 0 ? {done: true} : {value: i--, done: false};
}
}
}
};
let iterator = iterable[Symbol.iterator]();
log(iterator.next());
log(iterator.next());
log(iterator.next());
{value: 3, done: false}
{value: 2, done: false}
{value: 1, done: false}
- 위와 같은 iterable 객체를 정의했다고 하자.
- next 메서드를 정의해 값을 가져오지만 위와 같은 이터러블은 잘 만든 이터러블이라 할 수 없음,
for (const a of iterator) log(a);
error :VM2936:5 Uncaught TypeError: iterator is not iterable
at <anonymous>:5:19
- 이터레이터를 정의해주지 않아 for.. of문으로 실행 시, 이터레이터가 없다는 에러를 발생한다.
- 콘솔로 iterator 변수를 찍어보면 {next: ƒ}가 출력되는데, next 메서드만 정의되어 있고 Symbol은 정의되어 있지 않아 위와 같은 에러가 출력 된다.
2-2 잘 만든 사용자 정의 이터러블
const iterable = {
[Symbol.iterator]() {
let i = 3;
return {
next() {
return i == 0 ? {done: true} : {value: i--, done: false};
},
[Symbol.iterator]() {
return this;
}
}
}
};
for (const a of iterator) log(a);
3
2
1
2-3 DOM에서의 이터러블
for (const a of document.querySelectorAll('*')) log(a);
const all = document.querySelectorAll('*');
let iter3 = all[Symbol.iterator]();
log(iter3.next());
log(iter3.next());
log(iter3.next());
3. 전개 연산자
const a = [1,2];
log(...a, ...[3,4])
a[Symbol.iterator] = null
log(...a, ...[3,4])
1, 2, 3, 4
error :Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))