자바스크립트 고차함수 이해하기

Jiumn·2023년 4월 24일

JavaScript 대탐험

목록 보기
11/18

자바스크립트에는 map, filter, reduce 같은 고차함수들이 내장되어 있다. 그동안 고차함수에 대해서는 깊이 생각하지 않고 공식처럼 외워 쓰고 있었는데, <렛츠 기릿 자바스크립트 프로그래밍>이라는 책을 보다가 고차함수를 정리할 필요성을 느껴 포스팅해본다.


고차함수란?

고차함수(高次函數, Higher-Order Function)란 함수를 인자로 전달받거나 함수를 결과로 반환하는 함수를 말한다. 예를 들어 다음과 같은 함수 2개가 있다고 하자.

const func = (msg) => () => {
    console.log(msg)
}

const innerFunc = (msg) => {
    console.log(msg)
}

func 함수와 innerFunc 함수 모두 화살표 함수지만 첫 번째 함수는 화살표가 매개변수와 화살표가 2개라는 점에서 다르다.

즉, func 함수는 매개변수를 받아 다시 한번 함수를 반환하는 함수인 것이다. 이러한 함수를 고차함수라고 부른다.

func 함수는 내부에 또 다른 함수를 호출하는데, 매개변수를 다르게 하고 각각 변수에 할당함으로서 함수를 새롭게 만들어낼 수 있다.

func 함수에 서로 다른 매개변수를 전달했을 때 어떤 함수가 반환되는지 살펴보자.

const func = (msg) => () => {
    console.log(msg)
}

const one = func(1)
const two = func(2)
one() // 1
two() // 2

이처럼 func라는 고차함수가 있을 때, msg를 콘솔에 나타내는 함수를 만들어낼 수 있다. 여기서 중요한 것은 msg에 어떤 인자를 넣느냐에 따라 다른 함수를 만들어낼 수 있다는 점이다.

console.log(1)을 찍는 함수 one을 만들거나 console.log(2)를 찍는 함수 two를 만들 수도 있다.

그리고 one(), two()와 같이 괄호를 붙여 함수를 호출하면 최종적으로 의도했던 값이 반환된다.

여기서 고차함수를 쓰는 이유를 알 수 있다. 특정 부분만 다른 함수가 여러 번 사용되어야 하는 경우, 코드의 중복을 제거하고 재사용성을 높이기 위한 것이다.

다음 고차함수의 결과를 예상해보자.

const hof = (a) => (b) => (c) => {
    return a + (b * c);
}

const first = hof(3)
const second = first(4)
const third = second(5)

console.log('first', first) // first [Function (anonymous)]
console.log('second', second) // second [Function (anonymous)]
console.log('third', third) // third 23

hof(3) 함수에는 매개변수인 a가 3으로 할당되어 first 변수에 할당된다.
다음으로 호출된 first(4) 함수는 매개변수 b에 4가 할당되고 second 변수에 할당된다.
다음으로 호출된 second(5) 함수는 매개변수 c에 5가 할당되고 third 변수에 할당된다.

고차함수가 필요한 이유

이번에는 <렛츠 기릿 자바스크립트 프로그래밍>에 나오는 계산기 만들기 예제를 살펴보자.

계산기에는 숫자, 연산자 등 서로 다른 동작을 하는 버튼들로 구성된 기계다. 1번 버튼을 누르면 1번이, 2번 버튼을 누르면 2번이 화면에 나타나야 하고, + 버튼을 누르면 + 연산을, - 버튼을 누르면 - 연산을 해야 한다. 이 동작을 자바스크립트로 구현하면 addEventListener 함수를 만들어 click 이벤트 등록하고, 클릭 시 동작할 콜백함수를 등록해야 한다.

결국 모두 버튼을 누를 때 해당 버튼의 텍스트 값이 화면에 나타나거나 연산되어야 한다는 로직은 동일하지만 해당 텍스트의 값이나 연산만 다른 것이다. 즉, 함수의 다른 로직은 동일한데 일부만 다른 것이다.

이럴 때, 고차함수가 쓰인다.

let numOne = '';
let operator = '';
let numTwo = '';
const $operator = document.querySelector('#operator')

const onClickOperator = (op) => () => {
  if (numOne) {
    operator = op;
    $operator.value = op;
  } else {
    alert('숫자를 먼저 입력하세요.');
  }
};

document.querySelector('#plus').addEventLiseter('click', onClickOperator('+');
document.querySelector('#plus').addEventLiseter('click', onClickOperator('-');
document.querySelector('#plus').addEventLiseter('click', onClickOperator('/');

onClickOperator라는 고차함수는 op를 매개변수로 받아서 다른 함수를 반환한다. 이때 numOne(처음으로 입력된 숫자)이 있으면 op가 operator(연산자)에 할당되고, operator가 표시되는 폼에 op가 나타난다. 아니라면 '숫자를 먼저 입력하세요'라는 경고창이 노출된다.

addEventListener 함수에 onClickOperator 함수가 콜백함수로 등록되어 있다. 그리고 인자로는 각각의 연산자가 전달된다.

고차함수와 일반함수의 차이: 이벤트 리스너의 콜백함수로 등록되었을 때

그렇다면 고차함수가 아니라 일반함수로 콜백함수를 등록하면 어떨까? 머리속으로 상상만 해보니 긴가민가해서 다음과 같이 고차함수를 일반함수로 바꾼 후 실행해봤다.

const onClickOperator = (op) => {
  if (numOne) {
    operator = op;
    $operator.value = op;
  } else {
    alert('숫자를 먼저 입력하세요.');
  }
};

document.querySelector('#plus').addEventLiseter('click', onClickOperator('+');

이 경우 브라우저 렌더링 되자마자 '숫자를 먼저 입력하세요.'라는 경고창 메시지가 먼저 출력됐다. 이벤트 리스너의 콜백함수는 호출을 대기하고 있어야 하는데, onClickOperator('+')으로 바로 실행이 되었기 때문이다.

사실 조금만 생각해보면 알 수 있는 명백한 차이지만 (내 머리는 돌멩이와 같아서 찰떡같이 이해하기가 넘나 어려운 것...^_ㅠ) 실제로 실행을 해보니 더욱 더 확실히 체감할 수 있었다.

결론. 고차함수는 코드의 중복을 줄이기 위해 사용된다. (특히 일부만 코드가 다를 때)

참고 자료

렛츠 기릿 자바스크립트 프로그래밍 (도서)

profile
Back-End Wep Developer. 꾸준함이 능력이다. Node.js, React.js를 주로 다룹니다.

0개의 댓글