인프런 함수형 프로그래밍과 JavaScript ES6+ 강의 들으면서 정리하는 곳
<aside> 💡 평가 : 코드가 계산(Evaluation) 되어 값을 만드는 것
일급 : - 값으로 다룰 수 있다. - 변수에 담을 수 있다. - 함수의 인자로 사용될 수 있다. - 함수의 결과로 사용될 수 있다.
일급함수 : - 함수를 값으로 다룰 수 있다. - 조합성과 추상화의 도구
고차함수 : - 함수를 값으로 다루는 함수 고차함수 종류 두 가지 : - 함수를 인자로 받아서 실행하는 함수
const apply1 = f => f(1);
const add2 = a => a+2;
log(apply(add2));
결과 3
const times = (f,n) => {
let i = -1;
while(++i < n) f(i);
}
times(log, 3)
결과 0 1 2
times(a => log(a+10), 3)
결과 10 11 12
- 함수를 만들어 리턴하는 함수 (클로저를 만들어 리턴하는 함수)
const addMaker = a => b => a + b;
const add10 = addMaker(10);
log(add10);
결과 b => a + b;
log(add10(5))
결과 15
기존과 달라진 리스트 순회
for(const a of list) {
console.log(a);
}
이걸 사용하면 Map Set도 순회 가능하다
## 이터러블/이터레이터 프로토콜
- 이터러블 : 이터레이터를 리턴하는 [Symbol.iterator]()를 가진 값
- 이터레이터 : {value, done} 객체를 리턴하는 next()를 가진 값
- 이터러블/이터레이터 프로토콜 : 이터러블을 for...of, 전개 연산자 등과 함께 동작하도록 규약
제너레이터 : 이터레이터이자 이터러블을 생성하는 함수
function *gen() {
yield 1;
yield 2;
yield 3;
}
let iter = gen();
log(iter[Symbol.iterator]() == iter);
log(iter.next());
log(iter.next());
log(iter.next());
for(const a of gen()) log(a);
자바스크립트에서는 어떠한 값이든 이터러블이면 순회할 수 있다. 제너레이터는 문장을 값으로 만들 수 있고 그 문장을 통해서 순회할 수 있는 값을 만들수 있기 때문에 자바스크립트에서는 제너레이터를 통해서 어떠한 상태나 값이든 순회할 수 있게 만들 수 있다.
제너레이터를 활용한 홀수찍기
function *infinity(i = 0) {
while(true) yield i++;
}
function *limit(l,iter){
for(const a of iter) {
yield a;
if(a == l) return;
}
}
function *odds(l){
for(const a of limit(l, infinity(1))) {
if(a % 2) yield a;
}
}
for(const a of odds(40)) log(a);
const [a, b, ...c] = [1,2,3,4,5,6];
a => 1
b => 2
c => [3,4,5,6]
함수형 map 함수
const map = (f, iter) => {
let res = [];
for(const a of iter) {
res.push(f(a));
}
return res;
}
log(document.querySelectorAll(*).map(el => el.nodeName));
// doucument.querySelectorAll는 array를 상속받은게 아니라서 내부에 map함수가 구현이
// 안되어있음 그래서 위처럼 하면 undefiend뜬다 하지만
map(el => el.nodeName, document.querySelectAll(*)));
// 이건 동작한다. 이거는 되는 이유가 document.querySelectAll(*) 이터러블 프로토콜을
// 따르기 때문
function *gen() {
yield 2;
if(false) yield 3;
yield 4;
}
log(map(a => a*a, gen()); // 4,16
let m = new Man();
m.set('a', 10);
m.set('b'',20);
log(new Map(map(([k,a]) => [k, a*2], m)));
함수형 filter 함수
const products = [
{name : '반팔티', price: 15000},
{name : '긴팔티', price: 20000},
{name : '후드티', price: 25000},
{name : '목티', price: 30000},
];
const filter = (f, iter) => {
let res = [];
for(const a of iter) {
if(f(a)) res.push(a);
}
return res;
}
log(...filter(p => p.price < 20000. products));
함수형 reduce함수
const products = [
{name : '반팔티', price: 15000},
{name : '긴팔티', price: 20000},
{name : '후드티', price: 25000},
{name : '목티', price: 30000},
];
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;
}
log(reduce((total_price, product) => total_price + product.price, 0, products));
함수형 함수들 중첩 사용과 함수형 사고
const products = [
{name : '반팔티', price: 15000},
{name : '긴팔티', price: 20000},
{name : '후드티', price: 25000},
{name : '목티', price: 30000},
];
const add = (a,b) => a+b;
log(reduce(add, map(p => p.price, filter(p => p.price < 20000, products))));
// 코드가 복잡해 보이지만 오른쪽에서 왼쪽으로 읽어나가면 된다.
log(reduce(add, filter(n => n < 20000, map(p => p.price, products))));
// 위에 쓴거랑 순서는 다르지만 값은 똑같다.
// 이런 식으로 중첩 함수들을 구현 할 때는 왼쪽에서 부터 하나하나 생각해서 내가 기대하는
// 값의 모양을 만들어주는 함수를 써서 오른쪽에 구현해준다고 생각하면 될 듯
</aside>