[JavaScript] 함수형 프로그래밍 (이터러블, 이터레이터)
ES6부터 리스트를 순회하는 방법의 차이가 생겼다.
console.log('Array');
const arr = [1, 2, 3];
for(const a of arr) console.log(a); // 1 2 3
console.log('Set');
const set = new Set([1, 2, 3]);
for(const a of set) console.log(a); // 1 2 3
console.log('Map');
const map = ([['a', 1], ['b', 2], ['c', 3]]);
for(const a of map) console.log(a); // ["a", 1] ["b", 2] ["c", 3]
for(const a of map.keys()) console.log(a); // a b c
for(const a of map.values()) console.log(a); // 1 2 3
for(const a of map.entries()) console.log(a); // ["a", 1] ["b", 2] ["c", 3]
# 이터러블/이터레이터 프로토콜
- 이터러블: 이터레이터를 리턴하는 [Symbol.iterator]() 를 가진 값
- 이터레이터: { value, done } 객체를 리턴하는 next() 를 가진 값 (리절트 객체)
- 이터러블 / 이터레이터 프로토콜: 이터러블을 for...of, 전개 연산자, 구조 분해 할당 등과 함께 동작하도록 만든 규약
arr[Symbol.iterator] // f values() { [native Code] }
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 iterable = {
[Symbol.iterator](){
let i = 3;
return {
next(){
return (i === 0) ? {done: true} : { value: i--, done: false };
}
}
}
}
let iterator = iterable[Symbol.iterator]();
iterator.next();
for(const a of iterable) console.log(a); // 2 1
for(const a of iterator) console.log(a); // TypeError: iterator is not iterable
이터러블 / 이터레이터 프로토콜을 사용하기위해 사용자가 정의할때는 위의 [Symbol.iterator]() 메서드를 정의하고 [Symbol.iterator]() 메서드는 next() 메서드를 반환해야하며 next() 메서드의 내용으로는 { value, done } 을 가지는 객체를 반환해야 한다.
하지만 위의 이터러블 / 이터레이터는 완벽하지 않다. Array나 Set, Map 등 다른 이터러블 프로토콜을 만족하는 것들의 내부를 보면 이터러블 프로토콜을 만족하면서 이터레이터 프로토콜을 만족한다. 그러한 이터러블 프로토콜을 well-formed 이터러블 프로토콜 이라고 한다.
const iterable = {
[Symbol.iterator](){
let i = 3;
return {
next(){
return (i === 0) ? {done: true} : { value: i--, done: false };
},
[Symbol.iterator]() { return this; }
}
}
}
let iterator = iterable[Symbol.iterator]();
iterator.next();
for(const a of iterable) console.log(a); // 2 1
for(const a of iterator) console.log(a); // 2 1
well-formed 이터러블 프로토콜은 이터러블을 for...of 문으로 순회를 해도 순회가되고 이터레이터로 만들어서 for...of 문으로 순회해도 순회가 된다. 또한 일정부분 next() 메서드를 통해 진행을 하고 순회를 해도 마찬가지로 순회가 된다.
console.log(document.querySelectorAll('*')) // NodeList(...) [...]
for(const a of document.querySelectorAll('*')) console.log(a); // <html>...</html> <head>...</head> ...
const all = document.querySelectorAll('*');
let iter = all[Symbol.iterator]();
iter.next(); // { value: html, done: false }
iter.next(); // { value: head, done: false }
iter.next(); // { value: script, done: false }
해당하는 이터러블 프로토콜은 오픈소스 라이브러리 뿐만 아니라 브라우저의 DOM 또한 이터러블 프로토콜을 만족한다.
# 전개연산자
const a = [1, 2];
// a[Symbol.iterator] = null; // null이라면 아래 코드 TypeError
console.log([...a, ...arr, ...set, ...map.keys()]); // [1, 2, 1, 2, 3, 1, 2, 3, "a", "b", "c"]
자바스크립트에서 ES6의 이터러블 / 이터레이터 프로토콜은 중요하다. 해당하는 프로토콜을 만족하면 기존에 ES5에서 사용하던 자료구조보다 더 다양하고 쉬운 방식으로 데이터들을 핸들링 할 수 있다.
'JavaScript' 카테고리의 다른 글
[JavaScript] 함수형 프로그래밍 (map, filter, reduce) (0) | 2022.03.31 |
---|---|
[JavaScript] 함수형 프로그래밍 (제너레이터, 이터레이터) (0) | 2022.03.31 |
[JavaScript] 함수형 프로그래밍 (일급함수, 고차함수) (0) | 2022.03.29 |
[JavaScript] ES6 chap.3 (0) | 2021.12.02 |
[JavaScript] ES6 chap.2 (0) | 2021.11.30 |