map함수는 이터러블 프로토콜을 따르고 있기 때문에 다형성이 굉장히 높다.
예를 들어서 아래코드를 보자. 아래함수를 사용해서 결과를 확인하면 NodeList라는 결과가 나온다. array처럼 생겼다.
log(document.querySelectorAll('*'));
array처럼 생겼지만 map함수를 사용할 수 없다. querySelectorAll(*)을 통해서 나온 값을 map을 통해 안에 있는 어떤 값을 수집하려고 하면 아래와 같은 에러가 뜬다. 말 그대로 map이라는 함수가 없다.
log(document.querySelectorAll('*').map(el => el.nodeName));
=>
Uncaught TypeError: document.querySelectorAll(...).map is not a function
at 1.index.html:47
원래는 이와 같이 array의 map을 통해서 값을 수집해서 뭔가를 만들어 낼 수가 있는데 위에서는 하지 못했다.
log([1, 2, 3].map(a => a + 1));
=>
[2, 3, 4]
왜 그렇냐? 아래코드를 보자. document.querySelector는 array를 상속받은 객체가 아니기 때문에 그렇다. map함수가 구현이 안되어 있기 때문에 그런데 실제로 확인을 해보면 map 함수가 없다.
log(document.querySelectorAll('*'));
코드를 보면 documentquerySelector가 이터러블 프로토콜을 따르고 있기 떄문에 아래와 같은 결과가 나온다.
log(map(el => el.nodeName, document.querySelectorAll('*')));
=>
["HTML", "HEAD", "SCRIPT", "SCRIPT", "BODY", "SCRIPT", "SCRIPT"]
즉 이 map함수는 array뿐만 아니라 이터러블 프로토콜을 따르는 많은 함수들을 사용할 수 있게 된다.
const it = document.querySelectorAll('*')[Symbol.iterator]();
log(it.next());
log(it.next());
log(it.next());
log(it.next());
log(it.next());
=>
{value: html, done: false}
{value: head, done: false}
{value: script, done: false}
{value: script, done: false}
{value: body, done: false}
그래서 이러한 함수를 사용할 수 있게된다. 아래와 같은 제너레이터가 있다면 yield를 통해서 아래와 같은 문장역시도 map을 사용할 수 있다. 즉 제너레이터 함수의 결과들에 대해서도 map을 할 수 있기 때문에 사실상 모든 것들을 map을 할 수 있다고 볼 수 있다.
function* gen() {
yield 2;
if (false) yield 3;
yield 4;
}
log(map(a => a * a, gen()));
=>
[4, 16]
그리고 웹API인 querySelector은(JS에 있는것이 아닌 브라우저에서 사용되는 값 즉 헬퍼함수들) 이런 함수들이 이터러블 프로토콜을 따르고 있기 때문에 계속해서 만들어 질 것이다.
그리므로 이터러블 프로토콜을 따르는 함수들을 사용한다는 것은 앞으로 많은 헬퍼함수들과의 조합성이 좋아진다는 이야기이기도 하다.
그래서 프로토타입 기반으로 혹은 클래스 기반으로 어떤 카테고리 안에 있는 값만 사용하는 것보다 훨씬 더 유연하고 다양성이 높다고 볼 수 있다.
이터러블 프로토콜을 따른 map의 다형성2
아래코드를 보자. Map을 선언했다.
a에 10을 담고 b에 20을 담았다. 그리고 m값은 이터러블이기 때문에 심볼 이터레이터를 했을 때 특정 이터레이터를 반환하고 next를 했을 때 값이 조회가 된다. 즉 key와 value를 entry로 또는 pair로 해주고 있다.
let m = new Map();
m.set('a', 10);
m.set('b', 20);
const it = m[Symbol.iterator]();
log(it.next());
log(it.next());
log(it.next());
즉 map함수에 이터러블 값으로 사용할 수 있다는 것이다. 그리고 보조함수를 전달했을 때 value가 array로 들어오기 때문에 구조분해하여 key와 value를 나누어서 받을 수 있고 키와 값을 보조함수에서 map에 맞춰서 똑같이 엔트리를 리턴하도록 해주고 로그를 찍으면 아래와 같은 결과가 나온다.
즉 동일한 let m= new Map(); 맵객체와 동일한 맵객체를 만드는데 map함수를 사용해서 안쪽에 있는 값이[k, a * 2] 바뀐 map객체를 만들 수가 있다.
let m = new Map();
m.set('a', 10);
m.set('b', 20);
log(new Map(map(([k, a]) => [k, a * 2], m)));
<출처 : 유인동 함수형 프로그래밍과 JavaScript ES6+>
https://www.inflearn.com/course/functional-es6/dashboard
함수형 프로그래밍과 JavaScript ES6+ - 인프런 | 강의
ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,
www.inflearn.com
'JavaScript > 함수형 프로그래밍과 JavaScript ES6+' 카테고리의 다른 글
About Reduce (0) | 2021.08.18 |
---|---|
About filter (0) | 2021.08.18 |
map, filter, reduce(map) (0) | 2021.08.17 |
odds(제너레이터와 이터레이터) & for of etc.. (0) | 2021.08.17 |
제너레이터와 이터레이터 (0) | 2021.08.17 |