기본적으로 우리가 알고 있는 자바스크립트의 map 함수의 경우 Array를 상속받은 객체만 사용할 수 있는 함수이다. 즉 일반적으로 사용하는 Array의 경우에는 prototype으로 map 함수가 구현이 돼있어 map 함수를 사용할 수 있지만,
document.querySelectorAll("*");
위와 같은 경우에는 이터러블 객체임에도 불구하고 Array를 상속받은 객체는 아니므로 map 함수를 사용할 수가 없다. 그러니 이터러블 프로토콜을 만족하는 객체라면 어떤 객체든 사용할 수 있는 map 함수를 한 번 만들어 보자.
const arr = [1, 2, 3, 4, 5];
const nodes = document.querySelectorAll("*");
const map = (f, iter) => {
let res = [];
for(const a of iter) {
res.push(f(a));
}
return res;
}
console.log(map(e => e * 3, arr)); // [3, 6, 9, 12, 15]
console.log(map(e => e.nodeName, nodes)); // ["HTML", "HEAD", "META", "META", "META", "TITLE", "BODY"]
이터러블 객체를 순회하는 for of 문을 이용해 이렇게 만들어주면, Generator 함수를 포함해 이터러블 프로토콜을 만족하는 모든 객체를 반복해 반환할 수 있다. filter 함수도 마찬가지다 filter 함수도 Array.prototype에 구현된 함수이므로, 모든 이터러블 객체가 사용할 수 있게 만들려면 아래와 같이 함수를 따로 만들어 주면 된다.
const arr = [1, 2, 3, 4, 5];
const nodes = document.querySelectorAll("*");
const filter = (f, iter) => {
let res = [];
for(const a of iter) {
if(f(a)) {
res.push(a);
}
}
return res;
}
console.log(filter(e => e > 2, arr)); // [3, 4, 5]
console.log(filter(e => e.nodeName === "META", nodes)); // [meta, meta, meta]
reduce 함수도 똑같다. 다만 reduce 함수는 시작 value를 주지 않았을 경우도 감안해서 함수를 만들어야 한다. 때문에 조건문을 걸어 만약 시작 value인 acc가 없을 경우(세 번째 파라미터가 없을 경우)에는 이터러블 값의 첫 번째 값을 next()로 뽑아 acc로 해주었다.
const arr = [1, 2, 3, 4, 5];
const add = (a, b) => a + b;
const reduce = (f, acc, iter) => {
if(!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for(const a of iter) {
acc = f(acc, a);
}
return acc;
}
console.log(reduce(add, 0, arr)); // 15
'WEB > JS' 카테고리의 다른 글
[JS] forEach, for in, for of의 차이 (1) | 2019.05.05 |
---|---|
[JS] 코드를 값으로 다루는 go, pipe, curry 함수 만들기 (0) | 2019.05.03 |
[JS] 콜스택과 이벤트루프 (0) | 2019.02.22 |
[JS] 커링(Currying) (0) | 2019.02.19 |
리액트 - 서버사이드 렌더링(SSR) (0) | 2019.01.16 |