Develop/JavaScript
이터레이터 & 제네레이터 ( iterator & generator )
KimBangg
2021. 8. 9. 21:45
1. ES5의 리스트 순회
- 일반적인 프로그래밍 언어와 같이 숫자라는 "키"로 순회 하였습니다.
const list = [1,2,3];
for ( var i = 0; i < list.length; i++ ) {
console.log(list[i]);
}
2. ES6의 리스트 순회
- ES6 에서는 가독성을 위해 for of의 사용을 권고합니다.
- 하지만 일반적인 경우와는 달리 "키"가 없는데, 어떤 방식으로 순회를 하는걸까요?
// 훨씬 간결하고, 선언적인 순회 방법
const ( const val of list ) {
console.log(val);
}
3. [ Array, Set, Map ] 을 통해 알아보는 이터러블 & 이터레이터
- 3-1 이터러블 & 이터레이터 이란?
- 자바스크립트의 [ 배열, 객체, 맵 ] 은 이터러블, 이터레이블 프로토콜을 따르는 방식으로 구현이 되어있습니다.- - 이터러블 ( iterable ) -
- 이터레이터를 관리하는 [Symbol.iterator()] 를 가진 값.
- - 이터레이터 ( iterator )
- - { value, done } 객체를 리턴하는 next()를 가진 값.
- - 이터레블 / 이터레이터 프로토콜
- 이터러블을 for .. of, 전개 연산자 등과 함께 동작 하도록 하는 규약
- - 이터러블 ( iterable ) -
- 3-2 자바스크립트 배열, 맵, 셋의 작동 방식
- 아래의 예시를 보면 arr는 key로 접근이 가능하지만, set과 Map은 key로 접근이 불가능합니다.
- 즉, 이 사실을 통해 for.. of는 키를 바탕으로 값에 접근 하지 않는다는 것을 알 수 있습니다.
-
// Array [ 키로 접근해서 순회가 가능 => console.log(a[0]) ] const arr = [1,2,3]; for ( const val of arr ) { console.log(val); } // set [ 반면에 Set & Map 은 키로 접근이 안된다. ] const set = new Set([1,2,3]); console.log(set[1]); // undefined for ( const ele of set ) { console.log(ele); } // Map const map = new Map([['a',1], ['b',2], ['c', 3]]); console.log(map[1]); // undefined for ( const obj of map ) { console.log(obj); }
- 작동 방식
- 그렇다면 어떻게 동작하는걸까요?
- arr, map, set은 모두 이터레이블 프로토콜을 기반으로 작동합니다.
-
const arr = [1,2,3]; arr[Symbol.iterator] = null; // output: TypeError: arr is not iterable for ( const val of arr ) { console.log(val); }
-
- 이터러블 프로토콜은 next를 포함하는 iterator 메소드로 구성이 되어있습니다.
- 그리하여, 값이 있는 경우 done : false & value 가 전달이 되고 없는 경우는 done : true 값이 전달됩니다.
-
let iterator = arr[Symbol.iterator](); iterator.next() // { value : 1, done : false } iterator.next() // { value : 2, done : false } iterator.next() // { value : 3, done : false } iterator.next() // { value : undefined, done : true }
-
const arr1 = [1,2,3]; let iter1 = arr[Symbol.iterator](); iterator.next() // { value : 1, done : false } iterator.next() // { value : 1, done : false } for ( const a of iter1 ) { console.log(a); // 3 }
-
4. 사용자 정의 이터러블
- 이터러블에 대한 보다 자세한 이해를 위해 하단의 코드를 작성 해보았습니다.
- 이터레이블 내부에는 총 길이, next(), iterator 를 포함 하고 있습니다.
const iterable = {
[Symbol.iterator]() {
let i = 3 // parameter.legnth
return {
// next는 인덱스와 done 여부를 반환하는 함수입니다.
next() {
return i === 0 ? {done : true} : { value : i-- , done : false }
},
// well-formed iterable 은 iterataor를 통해서 모두 구동이 가능 해야 합니다.
[Symbol.iterator]() {
return this;
}
}
}
}
let iterator = iterable[Symbol.iterator]();
// iterator & iterator 모두 구동 가능
for ( const a of iterator ) {
console.log(a);
}
5. 전개 연산자
const a = [1,2];
// a[Symbol.iterator] = null;
console.log([...a, ...[3,4]]);
console.log(a);
6. 제너레이터
- 제너레이터는 이터레이터이자 이터레이터를 생성하는 함수 입니다.
- 제너레이터는 순회할 값을 문장으로 표현하는 것이라고도 말할 수 있습니다.
function *gen() {
yield 1;
yield 2;
yield 3;
return 100;
}
let iter = gen();
iter[Symbol.iterator]();
console.log(iter.next()); // { value : 1, done : false }
console.log(iter.next()); // { value : 2, done : false }
console.log(iter.next()); // { value : 3, done : false }
console.log(iter.next()); // { value : 100, done : ture }
for ( const a of gen() ) console.log(a)
7. 제너레이터 & 이터레이터 ( odds )
- 제너레이터를 다음과 같이 응용하여 사용 할 수 있습니다.
function *limit(l, iter) {
for ( const a of iter ) {
yield a;
if ( a === l ) {
return;
}
}
function *infinity(i = 0) {
while ( true ) {
yield i++;
}
}
function *odds(ele) {
for ( const a of limit( ele, infinity(1)) ) {
if ( a % 2 ) {
yield i;
}
// if ( a === ele ) return;
}
}
let iter2 = odds(10);
console.log(iter2.next());
console.log(iter2.next());
// 평가 할 때만, 생성이 되기 때문에 브라우저가 멈추는 일은 없음.
let iter3 = infinity();
console.log(iter3.next());
console.log(iter3.next());
console.log(iter3.next());
let iter4 = limit(4, [1,2,3,4,5,6]);
// ...
for ( const a of odds(40) ) console.log(a);
8. 구조 분해, Spread
console.log(...odds(10));
console.log(...odds(10), ...odds(20));
const [head, ...tail] = odds(5);
console.log(head);
console.log(tail);
const [a, b, ...rest ] = odds(10);
console.log(a);
console.log(b);
console.log(rest);