JavaScript/함수형 프로그래밍과 JavaScript ES6+

이터러블 프로토콜을 따른 map의 다형성

느리지만 꾸준하게 2021. 8. 18. 00:12

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('*'));

document.querySelectorAll에는 map 함수가 없다.

코드를 보면 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