← All Articles

[Javascript] 고차함수

Posted on

고차함수

자바스크립트를 사용하다 보면 Array 객체에 내장된 고차함수가 유용하게 쓰이는 것을 자주 볼 수 있다.

조금은 어려운 개념이긴 하지만 고차함수, 일급함수, 순수함수와 같은 개념을 잘 정리하고 사용한다면 이러한 코드 구조를 조금은 더 이해하고 사용할 수 있다고 생각이 든다.

이런 동기를 가지고 이번 포스트에서는 일급함수, 순수함수의 개념을 훑고 고차함수의 개념과 내장함수 사용예제들을 살펴보고자 한다.



일급 객체( First-class Object )

Javascript에서는 함수를 일급 객체로 다룬다. 이 말의 의미는 Javascript 코드에서는 함수를 반환할 수 있으며, 함수를 인자로 받을 수 있다는 의미이다. 이러한 코드 구조 덕분에 Javascript는 함수형 프로그래밍으로 인기를 가지게 되었다.

일급 객체의 조건

  1. 변수나 데이터 구조에 할당할 수 있어야 한다.
  2. 객체의 인자로 넘길 수 있어야 한다.
  3. 객체의 반환값으로 제공할 수 있어야 한다.


순수 함수

javascript와 함수형 프로그래밍의 개념을 익히기 위해선 순수 함수의 개념과 필요성에 대해 이해하고 넘어갈 필요가 있다.

함수형 프로그래밍이란 특정 문제를 해결하기 위해 모든 과정을 순수 함수로 나누어 문제를 해결하는 기법을 일컫는다.

여기서 함수형 프로그래밍 패러다임에서 순수함수를 사용하는 이유는 부수 효과(Side effect)를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이기 위함이다. 순수함수의 조건을 보게 되면 어떠한 이유에서 순수함수를 사용하는지 더 많은 이해가 가능할 것이다.

순수함수 조건

  • 동일한 인자(매개변수)의 경우 항상 같은 반환값이 나와야 한다.
  • Side effect가 일어나선 안된다.
  • Return 값으로만 소통한다.
  • 순수함수는 평가 시점이 중요하지 않다 -> 순수함수가 아닐 경우는 동일한 인자가 들어오더라도 여러 시점에서 함수의 결과, 평가값이 변경될 수 있으나 순수함수는 동일 인자에 동일 반환값이 나오기 때문에 평가시점과 관계가 없는 것 (1번 조건과 일맥상통한듯?)
let c = 15

function pureFunc(a, b) {
  return a + b
}   //순수 함수

function nonpureFunc(a, b) {
  return a + b + c;
}   // 순수 함수가 아님 : c의 값에 따라 동일 인자임에도 다른 반환값이 만들어질 수 있음


고차함수

고차함수란 함수를 인자로 전달받거나 함수를 결과로 반환하는 함수를 일컫는다.

Javascript에서는 함수를 일급객체로 다루기 때문에 고차함수와 같은 형태의 함수를 구현할 수 있다.

일급함수의 개념을 알고 있다면 고차함수의 개념도 쉽게 이해할 수 있을 것이다.

Javascript Array 객체에는 내장함수로 이러한 고차함수들을 만들어뒀기 때문에 이러한 함수들의 사용에 익숙해진다면 코드를 훨씬 간편하게 짤 수 있다.


forEach

  • forEach 메소드는 for문 대신 간편한 코드 형태로 사용이 가능하다.
  • forEach 메소드 인자로 콜백함수를 작성해야함 -> 콜백함수 인자로는 item(현재 반복인덱스에 저장된 데이터값), index(현재 반복될 인덱스값), array(현재 배열)가 존재한다.
  • forEach 메소드는 for문과 달리 break문을 사용할 수 없음. 중간 순회 중단이 불가능.
let dataary = [21, 42, 33, 14, 25, 12, 37, 28, 16, 11];

for (const item of dataary)
    console.log(item * 2)

dataary.forEach((item) => console.log(item * 2))    // for문을 가독성 있도록 코드 구성 가능

map

  • 위에서 확인한 forEach 메소드는 배열을 순회하며 요소 값을 참조해 어떠한 반복과정을 진행하기 위한 함수이지만 map 메소드의 경우 배열을 순회하며 요소 값을 다른 값으로 매핑하는 함수이다.
  • map 함수는 원본 배열을 변경시키지 않는다. 즉, 원본 배열을 변경시켜 매핑하는 방식이 아니라 콜백함수의 반환값으로 새로운 배열을 생성한다.
  • 콜백함수 인자는 forEach와 동일하게 item, index, array가 존재한다.
let dataary = [21, 42, 33, 14, 25, 12, 37, 28, 16, 11];

let newary = dataary.map((item) => {
    return item * 2;
})  // 배열값의 2배 값으로 매핑한 배열 생성

let newary2 = dataary.map((item) => {
    if (item % 2) return item * 2;
    return item;
})  // 배열값이 홀수인 경우에만 2배 값, 짝수는 본래 값으로 매핑한 배열 생성

reduce

  • reduce 메소드의 경우 배열을 순회하며 이전의 콜백함수 반환값을 전달해 콜백함수를 반복 실행하는 형태를 띄고 있다. -> 따라서 많은 블로그 예시에서 누적 연산값으로 예시를 드는 경우가 많다.
  • reduce 첫 번째 인자로는 다른 내장 고차함수와 동일하게 콜백함수를 기입하며, 두번 째 인자로는 콜백함수에 전달할 인자 초깃값을 설정할 수 있다.
let dataary = [21, 42, 33, 14, 25, 12, 37, 28, 16, 11];

let result = dataary.reduce((acc, cur, idx) => {
    // console.log(acc, cur, idx)
    return acc + cur
}, 0)   // 누적 덧셈값
console.log(result)

let result2 = dataary.reduce((acc, cur, idx) => {
    // console.log(acc, cur, idx)
    return acc + cur
})  // 두 번째 인자 초깃값을 전달하지 않은 경우
console.log(result2)
  • reduce 두 번째 인자값으로 초깃값을 전달하지 않은 경우에는 자동으로 배열 첫 번째 인덱스값으로 초기화된다. 따라서 위의 코드의 result, result2의 결과는 동일하다.
let dataary = [21, 42, 33, 14, 25, 12, 37, 28, 16, 11];

let resultary = dataary.reduce((acc, cur, idx) => {
    acc.push(cur * 2);
    return acc;
}, [])

console.log(resultary)
  • reduce의 acc인자를 배열로 생성해 push해주게 되면 map 메소드를 자체적으로 구현할 수 있다. 또한 조건부를 붙여 push 과정을 거치게 되면 filter 고차함수 또한 구현이 가능하다.


LanguageJavascript