2023.04.09
자바스크립트 스터디 12회차
공부 사이트: https://poiemaweb.com/
11. 이터레이션 프로토콜
ES6에서 도입된 이터레이션 프로토콜은 데이터 컬렉션을 순회하기 위한 프로토콜이다.이를 준수한 객체는 for of문으로 순회할 수 있고, Spread 문법의 피연산자가 될 수 있다.
이터레이션 프로토콜에는 두 개의 프로토콜이 존재한다.
ㅇ 이터러블 프로토콜 : "순회 가능한 자료 구조" 를 위한 프로토콜
ㅇ 이터레이터 프로토콜 : "이터러블의 요소를 탐색하기 위한 포인터" 를 위한 프로토콜
이터러블은 데이터 공급자의 역할을 한다.
1) 이터러블
for of문 등의 반복문에서 순회 가능한 자료구조.
Symbol.iterator 메소드를 구현하거나, 프로토타입 체인에 의해 상속한 객체를 의미한다.
배열은 Symbol.iterator 메소드를 소유하므로, 이터러블 프로토콜을 준수한 이터러블이다.
Symbol.iterator 메소드는 next 메소드를 포함한 이터레이터를 반환한다.
const arr = [1,2,3];
console.log(Symbol.iterator in arr); // true
for(const item of arr) {
console.log(item);
}
그러나 일반 객체는 Symbol.iterator 메소드를 소유하지 않으므로, 이터러블이 아니다.
const obj = {a: 1, b: 2};
console.log(Symbol.iterator in obj); // false
for(const p of obj) {
console.log(p);
} // TypeError
물론 일반 객체가 이터러블 프로토콜을 준수하도록 구현하면 이터러블이 될 수 있다. ▶ 커스텀 이터러블
2) 이터레이터
이터레이터 프로토콜은 next 메소드를 소유한다. 해당 메소드 호출 시 이터러블을 순회하며 value / done 프로퍼티를 갖는 이터레이터 결과 객체를 반환한다.
- value : 현재 순회 중인 이터러블의 값 반환
- done : 이터러블의 순회 완료 여부 반환
const arr = [1,2,3];
const iterator = arr[Symbol.iterator]();
console.log('next' in iterator);
// value, done 프로퍼티를 갖는 이터레이터 결과 객체 반환
let iteratorResult = iterator.next();
console.log(iteratorResult); // {value: 1, done: false}
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
3) for of 문
for of 문은 내부적으로 이터레이터의 next 메소드를 호출하여 이터러블은 순회하며, next 메소드가 반환한 이터레이터 결과 객체의 value 프로퍼티 값을 for of 변수에 할당한다.
그리고 done 프로퍼티 값이 false이면 이터러블의 순회를 계속하고, true이면 순회를 중단한다.
// 배열
for (const item of ['a','b','c']){
console.log(item);
}
// 문자열
for (const letter of 'abcd'){
console.log(letter);
}
// Map
for (const [key, value] of new Map([['a','1'],['b','2'],['c','3']])){
console.log(`key : ${key}, value : ${value}`);
}
// Set
for (const val of new Set([1,2,3])){
console.log(val);
}
4) 커스텀 이터러블
위에서 `일반 객체가 이터러블 프로토콜을 준수하도록 구현하면 이터러블이 될 수 있다.` 라고 설명했다.
간단한 예제를 살펴보자.
① 커스텀 이터러블 구현
/*
일반 객체 fibonacci에 Symbol.iterator 메소드를 구현함.
Symbol.iterator 메소드는 next 메소드를 소유한 이터레이터를 반환해야 함.
next 메소드는 이터리에터 결과 객체를 반환함 (value, done)
pre와 cur는 주로 반복문에서 아용되는 변수로, 이전값과 현재값을 의미함
*/
const fibonacci = {
[Symbol.iterator]() {
let [pre, cur] = [0,1];
const MAX_VALUE = 10;
return {
// fibonacci 객체가 return 될 때마다 next 메소드 호출
next(){
[pre, cur] = [cur, pre + cur];
return {
value: cur,
done: cur >= MAX_VALUE
};
}
};
}
};
for (const num of fibonacci){
console.log(num);
}
for of 문은 done 프로퍼티가 true가 될 떄까지 반복하므로, cur값이 MAX_VALUE인 10보다 크거나 같아질 때 반복이 끝난다.
② 이터러블을 생성하는 함수
fibonacci 이터러블의 최대값을 외부에서 전달할 수 있도록 수정해보겠다.
const fibonacciFunc = function(MAX_VALUE){
let [pre, cur] = [0,1];
return {
[Symbol.iterator]() {
return{
next(){
[pre, cur] = [cur, pre + cur];
return {
value: cur,
done: cur >= MAX_VALUE
};
}
};
}
};
};
for (const num of fibonacciFunc(10)){
console.log(num);
}
③ 이터러블이면서 이터레이터인 객체를 생성
이터레이터를 생성하기 위해서는 이터러블의 Symbol.iterator 메소드를 호출해야 한다.
그러나 이터러블이면서 이터레이터인 객체를 생성하면 Symbol.iterator 호출이 불필요하다.
#이터러블: 순회 가능한 자료 구조
#이터레이터: 이터러블에서 next() 를 통해 추출한 각 요소
[Q&A] 헷갈렸던 것
Q.
배열은 이터러블이면서 이터레이터인 객체이다.
문자열은 이터러블이다.
둘의 차이는 무엇인가?
A.
이터러블이면서 이터레이터인 객체는, Symbol.iterator 메소드를 구현하고 호출할 때마다 자신을 반환하는 객체이다.
배열은 Symbol.iterator 메소드를 구현하고, 호출할 때마다 자기 자신인 배열을 반환하기 때문에 이터러블이면서 이터레이터인 객체이다.
그러나 문자열은 Symbol.iterator을 구현하고 있지만, 호출할 때 자기 자신을 반환하는 것이 아니라.. 문자를 반환하기 때문에 이터레이터는 아니다. (즉, 이터러블이다)
✅ 이터러블이면서 이터러블인 객체
= for of 루프에서 반복 가능하며, 반복하는 동안 이터레이터 객체로 자신을 반환하는 객체
# 배열, Map, Set은 이터러블이면서 이터레이터이다.
[example] 이터러블이면서 이터레이터인 객체를 생성하는 함수
const fibonacciFunc = function(MAX_VALUE){
let [pre, cur] = [0,1];
return {
[Symbol.iterator](){
// 자기 자신을 반환
return this;
},
next(){
[pre, cur] = [cur, pre + cur];
return {
value : cur,
done: cur >= MAX_VALUE
};
}
};
};
let iter = fibonacciFunc(10);
// next()를 사용할 수 있는건 next()를 소유하고 있는 이터레이터 뿐이다.
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
console.log(iter.next());
iter = fibonacciFunc(10);
// iter는 이터러블이다.
for (const num of iter){
console.log(num);
}
④ 무한 이터러블과 Laze evaluation(지연된 평가)
[example] 무한 이터러블을 생성하는 함수 → 무한 수열
/*
무한 이터러블을 생성하는 함수를 통해 무한 수열 구현
*/
const fibonacciFunc = function(){
let [pre, cur] = [0,1];
return {
[Symbol.iterator](){
return this;
},
next(){
[pre, cur] = [cur, pre + cur];
return {
value : cur,
// done 생략
};
}
};
};
for (const num of fibonacciFunc()){
if(num > 10000) break;
console.log(num);
}
// 무한 이터러블에서 3개만 추출
const [f1, f2, f3] = fibonacciFunc();
console.log(f1, f2, f3);
이터러블은 데이터 공급자의 역할을 한다고 했다.
배열, 문자열, Map, Set 등의 빌트인 이터러블은 데이터를 모두 메모리에 확보한 뒤에 동작한다.
그러나 이터러블은 지연 평가(Lazy evaluation)를 통해 값을 생성한다.
# 지연 평가(Lazy evaluation) : 평가 결과가 필요할 떄까지 평가를 늦추는 기법
예제의 fibonacciFunc 함수는 무한 이터러블을 생성하지만, 이는 데이터를 공급하는 매커니즘을 구현한 것이다.
실제로 데이터를 생성하는 것은 for of문이나 디스트럭처링 할당 때이다.
→ for of 문 : 이터러블을 순회하며 내부에서 이터레이터의 next 메소드를 호출할 때 데이터가 생성된다.
" 데이터가 필요할 때까지 생성을 지연하다가 필요한 순간에 데이터를 생성함 "
'프로그래밍 > js' 카테고리의 다른 글
ES lessons 13-14. Babel, Webpack (0) | 2023.04.09 |
---|---|
ES lessons 12. 제너레이터 (0) | 2023.04.09 |
ES6 lessons 10. 심볼 (0) | 2023.04.03 |
ES6 lessons 9. 프로미스 (0) | 2023.04.03 |
ES6 lessons 8. 모듈 (0) | 2023.04.03 |