📍 고차 함수 ( higher order function )
함수를 인자로 받거나 함수를 결과로 반환하는 함수입니다.
JavaScript
의 함수는 일급 객체이기 때문에 함수의 매개변수로 전달할 수 있으며, 리턴 값으로 사용할 수 있습니다.
1
2
3
4
5
const add = (x, y) => x + y;
const higherOrderFunction = (func, x, y) => func(x, y);
console.log(higherOrderFunction(add, 1, 2));
JavaScript
에서는 배열 메서드를 이용해서 고차 함수를 사용합니다.
( Array.prototype.forEach()
, Array.prototype.some()
, Array.prototype.reduce()
등 )
1
2
3
4
const func = (v, i, self) => console.log(v, i, self);
// 함수를 인자로 받는 "Array.prototype.forEach()" ( 고차 함수 )
["a", "b", "c", "d"].forEach(func);
클로저도 고차 함수의 하나의 예시라고 볼 수 있는 것 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const outer = () => {
// 자유 변수
let _count = 0;
// 클로저
const inner = () => ++_count;
return inner;
};
const myCount = outer();
console.log(myCount()); // 1
console.log(myCount()); // 2
console.log(myCount()); // 3
console.log(myCount()); // 4
console.log(myCount.count); // undefined
아래 예시는 고차 함수를 활용한 또 다른 예시입니다.
( 참고: « 자바스크립트 완벽 가이드 8장 248p » ( 데이비드 플래너건 지음, 한성용 옮김, 인사이트, 2022 ) )
1
2
3
4
5
6
7
8
9
10
11
12
const not = (func) => {
return (...args) => {
let result = func.apply(this, args);
return !result;
};
};
const isEven = (n) => n % 2 === 0;
const isOdd = not(isEven);
console.log(isEven(2)); // true
console.log(isOdd(2)); // false
📍 커링 함수 ( currying function )
코어 자바스크립트의 143p ~ 145p를 참고해서 예시를 만들었습니다.
여러 개의 인자를 받는 함수를 한번에 하나씩 인자로 받는 함수로 나눈 함수입니다.
1
2
3
const func = (x) => (y) => (z) => x + y + z;
console.log(func(1)(2)(3));
위 예시만 보면 활용법이 애매하다고 느껴질 수 있습니다.
여러 개의 매개변수를 받는 함수에 하나의 매개변수만 유동적이거나, 받을 인자를 라이브러리에서 제공하는 경우 유용합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// https://jsonplaceholder.typicode.com/users/1
// https://jsonplaceholder.typicode.com/posts/1
const setUrl = (baseUrl) => (target) => (id) =>
fetch(`${baseUrl}/${target}/${id}`);
// url과 target 고정 ( user 데이터 요청 )
const setTarget1 = setUrl("https://jsonplaceholder.typicode.com");
const setId1 = setTarget1("users");
// id 가변적
const fetchUser = setId1(Math.floor(Math.random() * 9) + 1);
fetchUser.then((res) => res.json()).then(console.log);
// url과 target 고정 ( post 데이터 요청 )
const setTarget2 = setUrl("https://jsonplaceholder.typicode.com");
const setId2 = setTarget2("posts");
// id 가변적
const fetchPost = setId2(Math.floor(Math.random() * 9) + 1);
fetchPost.then((res) => res.json()).then(console.log);
저희가 주로 사용하는 Redux
의 middleware
에서도 커링 함수를 이용한다고 합니다.
아래와 같은 커링 함수를 middleware
로 등록하면 dispatch
를 통해 reducer
에 가기 전에 logger
를 거쳐서 정해진 동작을 수행하고 reducer
로 이동하게 됩니다.
자세하게 설명하기엔 주제에 벗어나기 때문에 나중에 redux
포스팅을 하게되면 해당 포스트에서 설명하겠습니다.
그냥 “유명한 라이브러리에서도 커링 함수를 활용해서 기능을 구현한다”정도의 의미 전달을 위해 작성했습니다.
1
2
3
4
const logger = store => next => action => {
console.log(store.getState());
next(action);
}
📍 순수 함수 ( pure function )
side effect
가 없고 같은 인자를 넣으면 항상 같은 값을 반환하는 함수를 의미합니다.
함수형 프로그래밍에서도 주로 사용하는 원칙정도로 알고 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let n1 = 10;
let n2 = 10;
// (1) 비순수 함수
const changeNumber = (number) => {
n1 = number * 2;
};
// (2) 순수 함수
const getNumber = (number) => number * 2;
changeNumber(50);
n2 = getNumber(50);
console.log(n1); // 100
console.log(n2); // 100
위의 예시에서 결과적으로 둘 다 동일하게 변수를 변형시킵니다.
하지만 (1)
에서는 함수 자체에서 외부 변수를 변화시키는 side effect
가 발생했고, (2)
는 그렇지 않습니다.
“이게 뭐가 중요한지 그냥 결과만 같으면 되는거 아닌가?”라고 생각할 수 있지만 코드가 길어지고 가독성과 유지·보수가 중요해지는 시점에서는 (1)
과 (2)
는 확연한 차이가 있습니다.
(1)
의 경우에는 n1
이라는 변수에 의존적이고 만약 (1)
과 같이 n1
에 의존적인 함수가 여러 개가 있다면 어떤 순서로 어떻게 n1
이 변화하는지 파악하기가 정말 힘들어집니다.
반면 (2)
의 경우에는 side effect
가 없고 같은 인자에 항상 같은 값을 반환하기 때문에 주위 상황을 고려하지 않고 함수만으로 어떤 결과가 나오는지 추측하기 쉽습니다.
“모든 함수를 순수 함수로 만들겠다!”라는 것은 거의 불가능하지만 순수 함수로 만들 수 있다면 최대한 순수 함수로 만드는 방식을 지향하는 게 미래의 본인과 협업하는 다른 개발자들을 위한 좋은 방법이라고 생각합니다.
아마 이런식으로 순수 함수를 지향하는 코드를 작성하는 방식을 함수형 프로그래밍이라고 하는 것으로 알고 있습니다.
📍 콜백 함수 ( callback function )
다시 호출하는 함수라는 의미를 갖는 함수입니다.
대표적으로 특정 이벤트에 등록하는 함수, 비동기 처리 함수등이 있습니다.
1
2
3
4
5
6
7
8
9
10
11
// (1) 콜백 함수의 매개변수는 코드상으로는 알 수 없음
const callback = (e) => {
console.log(e);
};
const $button = document.createElement("button");
$button.type = "button";
$button.addEventListener("click", callback);
$button.click();
(1)
과 같은 함수를 콜백 함수라고 합니다.
즉, “click
이라는 이벤트가 발생하면 내가 정의한 함수(위의 경우 callback()
)을 호출해줘”라는 의미를 갖습니다.
여기서 볼만한 것은 callback()
의 매개변수입니다.
따지고 보면 저희가 addEventListener()
의 함수 구현에 대해 볼 수 없기 때문에 어떤 인자를 넣어줄지 알 수 없는게 맞습니다.
하지만 제가 e
를 사용한 이유는 명세서에 event
객체가 매개변수로 들어오도록 구현했다고 했기 때문에 그것을 믿고 사용하는 것 뿐입니다.
엄밀히 말하면 매개변수도 그렇고 this도 알 수 없는게 맞습니다.
📍 팩토리 함수 ( factory function )
객체를 반환하는 함수를 의미합니다.
제가 사용해본 경험으로는 redux
의 액션 크리에이터 함수입니다.
1
const createIncreaseCountAction = (number) => ({ type: "INCREASE", payload: number });
📮 레퍼런스
- « 코어 자바스크립트 5장 ( 143p ~ 145p ) » ( 정재남 지음, 위키북스, 2019 )
- « 자바스크립트 완벽 가이드 8장 ( 248p ) » ( 데이비드 플래너건 지음, 한성용 옮김, 인사이트, 2022 )