[JS/Node] 고차함수

First-class citizen ; 특별 대우 받는 함수

자바스크립트에서 특별한 대우를 받는 일급 객체(first-class citizen) 중 하나가 함수이다. 자바스크립트에서 함수는 아래와 같이 특별하게 취급된다.

  • 변수에 할당(assignment) 할 수 있음
  • 다른 함수의 인자 (argument)로 전달 될 수 있음
  • 다른 함수의 결과로서 리턴될 수 있음

함수를 변수에 할당할 수 있어서 함수를 배열의 요소나 객체의 속성값으로 저장할 수 있다.

함수를 데이터[ string / number / boolean / array / object ]를 다루듯이 다룰 수 있다는 의미


1. 변수에 함수를 할당하는 경우

1
2
3
4
5
6
7
8
9
10
11
/* **변수 square에 함수를 할당하는 함수 표현식**,
* 자바스크립트에서 함수는 일급 객체라 변수에 저장 가능함.
* But, 함수 표현식은 할당 전에는 사용할 수 없음. */

const square = function (num) {
return num * num;
};

// square에 함수(=일급객체)가 저장되어 있어서 함수 호출 연산자인 ()를 사용 가능
output = square(7);
console.log(output); // 49

위의 함수 표현식(function expression)은 함수 선언식(function declaration)과 다르게 [호이스팅(Hoistion)](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)이 적용되지 않음.

  • 호이스팅선언된 위치에 관계없이 어디서든 함수를 사용할 수 있게 함
  • 코드가 실행되는 과정에서 함수 선언부코드의 최상단으로 끌어올리는 것처럼 보이게 함

함수 선언식의 호이스팅에 지나친 의존 → 코드의 유지 보수가 쉽지 않음

= 코드 리뷰나 디버깅을 할 때, 코드를 왔다갔다하게 됨.


  • 함수 선언식
    • 어느 위치에나 함수를 선언
    • 함수의 실행 위치도 중요하지 x

But,

  • 함수 표현식
    • 함수의 할당과 실행 위치에 따라 결과가 달라짐
    • 코드의 위치를 어느정도 예측 가능

호이스팅을 제외하면, 함수 선언식과 함수 표현식은 크게 차이가 없다. 다만, 함수 표현식의 경우는 함수가 변수에 저장될 수 있다는 사실을 분명하게 보여준다.

그리고 함수는 변수에 저장된 데이터를 인자로 받거나, 리턴 값으로 사용할 수 있다. 함수도 변수에 저장될 수 있기 때문에 함수를 인자로 받거나, 리턴 값으로 사용할 수 있다.



고차 함수 ?

고차 함수(higher order function)는 함수를 인자(argument)로 받을 수 있고, 함수의 형태로 리턴할 수 있는 함수.

함수는 변수에 저장할 수 있다. 그리고 함수는 함수를 담은 변수를 인자로 전달 받을 수 있다. 마찬가지로, 함수 내부에서 변수에 함수를 할당할 수 있다. 그리고 함수는 이 변수를 리턴할 수 있다. 여기서 변수에 할당하지 않고 함수를 바로 이용할 수 있다. 어떤 고차 함수에 함수를 인자로 전달하고, 고차 함수는 함수 자체를 리턴한다. 변수가 빠졌을 뿐, 동일하게 동작한다.

이때 다른 함수(caller)의 인자(argument)로 전달되는 함수를 콜백 함수(callback function)라고 한다. 어떤 작업이 완료되었을 때 호출하는 경우가 많다.

콜백 함수를 전달받은 고차 함수함수 내부에서 이 콜백 함수를 호출(invoke) 할 수 있다. caller조건에 따라 콜백 함수의 실행 여부를 결정할 수 있다. 호출하지 않을 수도 있고, 여러 번 실행할 수도 있다.

함수를 리턴하는 함수‘를 고안해 낸 논리학자 하스켈 커리의 이름을 따 커리 함수라고 한다. 따로 커리 함수라는 용어를 사용하는 경우에 고차 함수란 용어를 ‘함수를 인자로 받는 함수‘에만 한정해서 사용하기도 한다. 그러나 정확하게는 고차 함수커리 함수를 포함한다.


1. 다른 함수를 인자로 받는 경우

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function double (num) {
return num * 2;
}

function doubleNum (func, num) {
return func(num);
}

/* 함수 doubleNum은 다른 함수를 인자로 받는 고차 함수.
* 함수 doubleNum의 첫번째 인자 func에 함수가 들어오면
* 함수 func은 함수 doubleNum의 콜백 함수. */

let output = doubleNum(double, 4);
console.log(output); // = 8

// 함수 double은 함수 doubleNum의 콜백 함수.

2. 함수를 리턴하는 경우

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function adder (added) {
return function (num) {
return num + added;
};
}

/* 함수 adder은 다른 함수를 리턴하는 고차 함수.
* adder은 인자 한개를 입력받아서 익명 함수를 리턴한다.
* 리턴되는 익명 함수는 인자 한개를 받아 added와 더한 값을 리턴 */

// adder(5)는 함수라 함수 호출 연산자 '()'를 사용할 수 있음
let output = adder(5)(3); // 8
console.log(output);

// adder가 리턴하는 함수를 변수에 저장할 수 있음
// 자바스크립트에서 함수는 일급 객체이기 때문
const add3 = adder(3);
output = add3(2);
console.log(output); // 5

3. 함수를 인자로 받고, 함수를 리턴하는 경우

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function double (num) {
return num * 2;
}

function doubleAdder (added, func) {
const doubled = func(added);
return function (num) {
return num + doubled;
};
}

/* 함수 doubleAdder는 고차 함수.
* 함수 doubleAdder의 인자 func는 doubleAdder의 콜백 함수.
* 함수 double은 함수 doubleAdder의 콜백으로 전달. */

// doubleAdder(5, double)는 함수라 함수 호출 기호 '()' 사용
doubleAdder(5, double) (3); // 13

// doubleAdder가 리턴하는 함수를 변수에 저장할 수 있음.(일급 객체)
const addTwice3 = doubleAdder(3, double);
addTwice3(2); // 8