일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- javascript
- 싱글 스레드
- node.js
- 파이썬
- Array
- 패널 교체
- Android
- js
- Kotlin
- 노트북
- 함수
- adapter
- 리스트 뷰
- HP
- 노트북 추천
- 자바스크립트
- Java
- Overloading
- 자바
- golang
- go
- 코틀린
- var
- 안드로이드
- as?
- Python
- 오버로딩
- ListView
- 배열
- 연산자
- Today
- Total
Bbaktaeho
[Javascript] Generator, Iterator, Iterable (제너레이터, 이터레이터, function*) 본문
[Javascript] Generator, Iterator, Iterable (제너레이터, 이터레이터, function*)
Bbaktaeho 2020. 10. 17. 01:05들어가며
※ FxJS 라이브러리를 개발하신 유인동 강사님의 함수형 프로그래밍 강의를 듣고 정리한 글임을 밝힙니다.
함수형 프로그래밍 자바스크립트 페이스북 커뮤니티입니다.
ko-kr.facebook.com/groups/539983619537858/
iterable, iterator
iterable : 반복 가능한 / iterator : 반복자
자바스크립트에서 배열은 반복 가능한 객체(object)입니다. 반복이 가능하다는 것은 iterable이라고 부르기도 합니다.
자바스크립트는 itrable 요소들만 순회할 수 있는 문법이 있습니다.
for of 반복문인데요. 이 반복문은 iterable 요소들만 반복할 수 있고 그렇지 않은 요소들로 반복하려고 할 때
TypeError: 요소 is not iterable 에러를 던집니다. (for in 반복문은 일반 객체도 순회합니다!)
const test = { a: 1, b: 2 };
for (item of test) {
console.log(item);
}
// TypeError: test is not iterable
좀 더 상세하게 설명하자면
iterable은 iterator를 리턴하는 [Symbol.iterator]() 를 가진 값,
iterator는 {value, done} 객체를 리턴하는 next() 를 가진 값입니다.
value는 값으로서 여러 타입이 올 수 있고 done은 boolean 타입을 가집니다.
const arr = [1, 2, 3, 4];
const set = new Set([1, 2, 3, 3, 5, 2]);
const map = new Map([
['a', 'A'],
['b', 'B'],
]);
const obj = { a: 1, b: 2 };
console.log(arr[Symbol.iterator]());
console.log(set[Symbol.iterator]());
console.log(map[Symbol.iterator]());
console.log(obj[Symbol.iterator]());
모든 자바스크립트 코드는 node로 실행하겠습니다.
위 코드를 실행하면
Object [Array Iterator] {}
[Set Iterator] { 1, 2, 3, 5 }
[Map Entries] { [ 'a', 'A' ], [ 'b', 'B' ] }
console.log(obj[Symbol.iterator]());
^
TypeError: obj[Symbol.iterator] is not a function
console.log(obj[Symbol.iterator]()); 코드에서 에러가 발생합니다.
즉, 일반적인 객체는 iterable이 아닌 것을 알 수 있습니다.
generator
generator : 발전기, 발생기
자바스크립트에는 generator가 있습니다.
자바스크립트에서 generator는 이터레이터를 발생시키는 함수로 사용됩니다.
구현은 function* 키워드를 사용해서 코드를 구현합니다.
function* gen() {}
// generator 함수로 이름은 gen이며 이터러블을 생성하는 함수
제너레이터 함수 내부에 yield 키워드를 사용해서 이터러블의 요소를 표현식으로 나타낼 수 있습니다.
function* gen() {
yield 1;
yield 2;
yield 3;
return -1;
}
const iter = gen();
for (item of iter) console.log(item);
위 코드를 실행시키면
1
2
3
마치 배열을 순회해서 요소들을 출력한 것처럼 로그에 출력됩니다.
yield 키워드가 이터러블의 요소가 되는 것을 볼 수 있습니다. 쉽게 말해서 마치 배열처럼 yield 옆에 표현식이 배열의 요소가 되는 거라고 생각해볼 수 있습니다. (실제 배열은 아님)
그럼 return은 어디에서 확인할 수 있는지 궁금할 수 있습니다.
return 된 값은 어디로 갔는지 알아보기 전에 먼저 [Symbol.iterator]() 부터 짚고 가겠습니다.
Symbol를 설명하면 글이 길어질 것 같아서 간단하게 설명하자면 객체의 속성을 만들 수 있는 데이터 타입으로 이해하면 됩니다.
그중, Symbol.iterator는 객체의 iterble 속성을 정의하기 위해 쓰입니다.
직접 일반 객체를 iterable 객체로 구현할 수도 있습니다.
const iterable = {
[Symbol.iterator]() {
let i = 3;
return {
next: () => i == 0 ? {value: undefined, done: true} : {value: i--, done: false},
[Symbol.iterator]() {return this}
}
}
}
const it = iterable[Symbol.iterator]();
for(let i of it) console.log(i);
코드를 실행하면
3
2
1
순서대로 3, 2, 1이 콘솔에 출력됩니다.
Symbol.iterator를 객체의 속성으로 지정하고 함수로 사용하는 모습입니다.
이 함수는 객체를 리턴하며 리턴된 객체는
{
next: [Function: next],
[Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]]
}
위와 같은 모습을 가지고 있습니다.
여기서 next 함수를 통해 {value, done} 객체를 가져오게 됩니다. 즉, iterator라고 할 수 있습니다(위에 설명).
다시 돌아와서 제너레이터의 return 값은 done이 true일 때 알 수 있습니다.
for of 반복문으로 순회하기 전에 제너레이터는 이터레이터니까 next() 함수를 실행해서 결과를 확인하겠습니다.
function* gen() {
yield 1;
yield 2;
yield 3;
return -1;
}
const iter = gen();
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
코드를 실행하면
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: -1, done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
{value, done} 객체가 출력됩니다.
여기서 유추할 수 있는 게 for of 반복문으로 이터러블 객체를 순회했을 때 done이 false일 때의 value 값을 순회한 것을 알 수 있고 done이 true일 때는 return 받은 값이 value에 할당된 것을 볼 수 있습니다. return이 되면 이후 value 값은 undefined 값으로 할당되네요.
실제 for of 반복문을 일반 반복문으로 변경한다면
const arr = [1, 2, 3, 4, 5, 6, 7];
// for of
for (const item of arr) {
console.log(item);
}
// for of 동작 과정
const iter = arr[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const item = cur.value;
console.log(item);
}
이터레이터의 next 함수를 실행시켜서 value 값을 추출하는 것으로 나타낼 수 있습니다.
전개 연산자(...)를 사용해서 done이 false일 때 value 값들을 전개할 수도 있습니다.
console.log(...iter);
// 1 2 3
여기까지 본다면 왜 제너레이터를 사용하는지 알기 어렵습니다.
다음 포스팅에서 제너레이터를 이용해서 얻는 장점에 대해서 글을 쓰도록 하겠습니다.
'프로그래밍 (Programming) > 자바스크립트 (JavaScript)' 카테고리의 다른 글
[Javascript] 기본 제공 API (자바스크립트 팁) (0) | 2020.10.24 |
---|---|
[Javascript] Generator 활용과 장점 (iterable, iterator, lazy evaluation) (0) | 2020.10.21 |
[Javascript] null 병합 연산자 '??' 와 OR 연산자 '||' 의 차이 (자바스크립트, nullish coalescing, falsy값) (2) | 2020.08.24 |
[Javascript/Node.js] 싱글 스레드 프로그래밍 (uncaughtExcetion) (0) | 2020.05.26 |
[Javascript/Node.js] 비동기 프로그래밍 (자바스크립트, 비동기, 노드, nextTick) (0) | 2020.05.25 |