맨틀 이야기
고차 함수 알아보기 본문
고차 함수(higher-order function)는 함수를 인자로 받거나 함수를 리턴값으로 주는 함수를 말한다. 함수를 인자로 받는 경우는 많이 보고 써봤는데, 함수를 리턴하는 함수는 실전에서 써 본 적이 없어서 확실히 알아보려고 한다.
Closure
함수가 함수를 리턴할 경우, 그 리턴된 함수를 어떻게든 사용하려면 일단 변수에 저장해야 한다. 변수에 저장한 뒤에 해당 변수를 함수로 호출하면 고차 함수가 리턴한 함수가 실행된다.
function greet() {
return function () {
console.log("hello, world");
};
}
const sayHello = greet(); // sayHello 변수에 저장
sayHello(); // "hello, world" 출력
이렇게 고차 함수 안에서 리턴값으로 배출되는 내부 함수를 클로저 함수라고 한다.
클로저는 선언된 함수와 그 함수를 만든 환경(lexical environment)이 이루는 하나의 조합이다. 따라서 클로저 함수는 고차 함수의 스코프를 자신이 속한 lexical 환경으로 사용한다. 고차 함수 안에 선언된 로컬 변수나 함수를 클로저 함수도 참조할 수 있다는 뜻이다.
위 예시에서 고차 함수 안에 firstName이라는 변수를 선언한 뒤에 클로저 함수가 firstName 변수를 활용하도록 하면, 클로저 함수가 정상적으로 고차 함수의 변수 값에 접근할 수 있다는 것을 확인할 수 있다. 또한 클로저 함수는 고차 함수의 스코프 외에도 글로벌 환경에 선언된 전역 변수인 lastName까지 참조할 수 있다.
function greet() {
const firstName = "Sam"; // greet() 함수의 스코프
return function () {
console.log("hello, " + firstName + " " + lastName);
};
}
const sayHello = greet();
const lastName = "Adams"; // 전역 스코프
sayHello(); // "hello, Sam Adams" 출력
Currying
고차 함수와 클로저 개념을 활용한 또다른 기술로는 커링(currying)이 있다. 함수가 함수를 리턴할 수 있고, 리턴되는 함수는 상위 스코프의 변수를 참조할 수 있다는 점을 이용해 인자를 여럿 받는 함수를 인자를 하나씩만 받는 중첩된 함수로 나누는 것을 커링이라고 한다.
// 커링하기 전
function add(a, b, c) {
return a + b + c;
}
// 커링한 후
function add(a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}
const add1 = add(1);
const add1and2 = add1(2);
add(1)(2)(3); // 6
add1(2)(3); // 6
add1and2(3); // 6
위 코드를 보면 커링한 add() 함수는 b를 인자로 받는 함수를 리턴하고, 그 함수는 c를 인자로 받는 함수를 리턴한다. 제일 안쪽에 nesting된 함수는 상위 함수들의 인자(a, b)를 참조해 c와 더한 최종 값을 리턴한다.
커링된 함수를 호출할 땐 nesting된 함수의 수만큼 괄호를 연속으로 붙여 호출할 수 있다. 함수를 리턴하는 것이기 때문에 괄호를 추가로 달아 리턴된 함수를 바로 실행하게 되는 것이다. (이런 호출 방법은 클로저 함수도 동일하다)
커링은 하나의 함수로부터 인자만 바꿔 다양한 파생 함수를 만들 때 유용하게 쓸 수 있다.