군만두의 IT 개발 일지

[DAY6] JavaScript 고차 함수 본문

개발일지/SW직무역량 부트캠프

[DAY6] JavaScript 고차 함수

mandus 2023. 7. 6. 17:01

목차

    🚀 진행기간: 2023년 6월 26일 ~ 2023년 7월 13일

    ⭐요약


    JavaScript에서 고차 함수(Higher Order Function)와 내장 고차 함수, 그리고 추상화 관점에서의 고차 함수의 중요성에 대해 정리한다.

    더보기
    더보기

     

    Chapter 1. 고차 함수 함수는 일급 객체이므로 변수에 할당하거나 다른 함수의 인자·리턴값으로 사용할 수 있음.
    고차 함수는 함수를 인자로 받거나 함수를 리턴하는 함수이며, 인자로 전달되는 함수를 콜백 함수라 함.
    Chapter 2. 내장 고차 함수 filter: 조건을 만족하는 요소만 걸러내 새 배열로 반환함.
    map: 모든 요소에 동일한 동작을 적용시켜 새 배열로 반환함.
    reduce: 배열의 여러 요소를 하나의 값으로 응축함.
    Chapter 3. 고차 함수의 중요성 고차 함수는 사고(함수) 수준에서의 추상화를 달성하여 코드의 재사용성과 생산성을 높임.
    각각의 함수를 compose로 조합하면 복잡한 로직도 선언적으로 표현할 수 있음.

     

    ⭐고차 함수(Higher Order Function)


    1. 일급 객체

    JavaScript에서 함수는 대표적인 일급 객체(First-Class Object)이다. 일급 객체란 아래 세 가지 특징을 모두 만족하는 객체를 말한다.

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

    2. 고차 함수의 이해

    • 고차 함수(Higher Order Function): 함수를 전달인자(argument)로 받을 수 있고, 함수를 리턴할 수 있는 함수이다.
    • 다른 함수(caller)의 전달인자로 전달되는 함수를 콜백 함수(callback function)라고 한다.

    🚀 고차 함수의 세 가지 형태

     

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

    function double(num) {
      return num * 2;
    }
    
    function doubleNum(func, num) {
      return func(num);
    }
    
    /*
     * 함수 doubleNum은 다른 함수를 인자로 받는 고차 함수입니다.
     * 함수 doubleNum의 첫 번째 인자 func에 함수가 들어올 경우
     * 함수 func는 함수 doubleNum의 콜백 함수입니다.
     * 아래와 같은 경우, 함수 double은 함수 doubleNum의 콜백 함수입니다.
     */
    let output = doubleNum(double, 4);
    console.log(output); // -> 8

    2) 함수를 리턴하는 경우

    function adder(added) {
      return function (num) {
        return num + added;
      };
    }
    
    /*
     * 함수 adder는 다른 함수를 리턴하는 고차 함수입니다.
     * adder는 인자 한 개를 입력받아서 함수(익명 함수)를 리턴합니다.
     * 리턴되는 익명 함수는 인자 한 개를 받아서 added와 더한 값을 리턴합니다.
     */
    
    // adder(5)는 함수이므로 함수 호출 연산자 '()'를 사용할 수 있습니다.
    let output = adder(5)(3); // -> 8
    console.log(output); // -> 8
    
    // adder가 리턴하는 함수를 변수에 저장할 수 있습니다.
    // javascript에서 함수는 일급 객체이기 때문입니다.
    const add3 = adder(3);
    output = add3(2);
    console.log(output); // -> 5

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

    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

    ⭐내장 고차 함수


    1. filter

    • filter: 배열의 모든 요소 중에서 특정 조건을 만족하는 요소만 걸러내 새 배열로 반환한다.
    • 특정 조건은 filter 메서드의 전달인자로 함수 형태로 전달된다. 따라서 filter는 고차 함수이다.
    • 원본 배열은 변경되지 않는다.
    // 함수 표현식
    const isEven = function (num) {
      return num % 2 === 0;
    };
    
    let arr = [1, 2, 3, 4];
    // '짝수'를 판별하는 함수가 조건으로서 filter 메서드의 전달인자로 전달됩니다.
    let output = arr.filter(isEven);
    console.log(output); // ->> [2, 4]
    
    const isLteFive = function (str) {
      // Lte = less than equal
      return str.length <= 5;
    };
    
    arr = ['hello', 'code', 'states', 'happy', 'hacking'];
    // '길이 5 이하'를 판별하는 함수가 조건으로서 filter 메서드의 전달인자로 전달됩니다.
    let output2 = arr.filter(isLteFive);
    console.log(output2); // ->> ['hello', 'code', 'happy']

    2. map

    • map: 하나의 데이터를 다른 데이터로 매핑(mapping)할 때 사용한다.
    • 모든 요소에 동일한 동작을 적용시킨 뒤 새 배열로 반환한다.
    • 원본 배열은 변경되지 않는다.
    const cartoons = [
      {
        id: 1,
        bookType: 'cartoon',
        title: '식객',
        subtitle: '어머니의 쌀',
        createdAt: '2003-09-09',
        genre: '요리',
        artist: '허영만',
        averageScore: 9.66,
      },
      {
        id: 2,
        // .. 이하 생략
      },
      // ... 이하 생략
    ];
    
    // 만화책 한 권의 부제를 리턴하는 로직(함수)
    const findSubtitle = function (cartoon) {
      return cartoon.subtitle;
    };
    
    // 각 책의 부제 모음
    const subtitles = cartoons.map(findSubtitle); // ['어머니의 쌀', ...]

    3. reduce

    • reduce: 여러 데이터를 하나의 데이터로 응축(reduce)할 때 사용한다.
    function joinName(resultStr, user) {
      resultStr = resultStr + user.name + ', ';
      return resultStr;
    }
    
    let users = [
      { name: 'Tim', age: 40 },
      { name: 'Satya', age: 30 },
      { name: 'Sundar', age: 50 }
    ];
    
    users.reduce(joinName, '');

    ⭐고차 함수의 중요성


    1. 고차 함수를 쓰는 이유 — 추상화

    • 추상화(Abstraction): 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것을 말한다.
    • 추상화는 생산성(productivity)의 향상으로 이어진다.

    🚀 함수와 고차 함수의 추상화 수준 비교

    구분 설명 추상화 수준
    함수 값을 전달받아 값을 리턴함. 값에 대한 복잡한 로직은 감추어져 있음 값(value) 수준의 추상화
    고차 함수 함수를 전달받거나 함수를 리턴함. 사고(함수)에 대한 복잡한 로직은 감추어져 있음 사고(thought) 수준의 추상화

    고차 함수는 추상화의 수준을 값 수준에서 사고의 수준으로 끌어올린다.

    2. 고차 함수와 추상화

    아래 코드는 여러 함수를 순서대로 조합하는 고차 함수 compose의 예시이다. 각각의 함수는 단일 목적을 가지며, 다른 목적을 위해 재사용(reuse)될 수 있다.

    function getOnlyMales(data) {
      return data.filter(function (d) {
        return d.gender === 'male';
      });
    }
    
    function getOnlyAges(data) {
      return data.map(function (d) {
        return d.age;
      });
    }
    
    function getAverage(data) {
      const sum = data.reduce(function (acc, cur) {
        return acc + cur;
      }, 0);
      return sum / data.length;
    }
    
    function compose(...funcArgs) {
      // compose는 여러 개의 함수를 인자로 전달받아 함수를 리턴하는 고차 함수입니다.
      // compose가 리턴하는 함수(익명 함수)는 임의의 타입의 data를 입력받아,
      return function (data) {
        // funcArgs의 요소인 함수들을 차례대로 적용(apply)시킨 결과를 리턴합니다.
        let result = data;
        for (let i = 0; i < funcArgs.length; i++) {
          result = funcArgs[i](result);
        }
        return result;
      };
    }
    
    // compose를 통해 함수들이 순서대로 적용된다는 것이 직관적으로 드러납니다.
    // 각각의 함수는 다른 목적을 위해 재사용(reuse) 될 수 있습니다.
    const getAverageAgeOfMale = compose(
      getOnlyMales, // 배열을 입력받아 배열을 리턴하는 함수
      getOnlyAges,  // 배열을 입력받아 배열을 리턴하는 함수
      getAverage    // 배열을 입력받아 `number` 타입을 리턴하는 함수
    );
    
    const result = getAverageAgeOfMale(data);
    console.log(result); // --> 26

    ⭐후기


    • 일급 객체와 고차 함수의 개념을 처음 접했을 때는 낯설었지만, 콜백 함수 예를 통해 이미 사용해 왔다는 것을 깨달았다.
    • filter, map, reduce는 앞으로 데이터를 다룰 때 많이 사용하게 될 메서드인 만큼, 세 가지의 차이점과 활용법을 확실히 익혀야겠다.

    ⭐참고자료


    1) MDN Web Docs, "Array.prototype.filter()", https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

    2) MDN Web Docs, "Array.prototype.map()", https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map

    3) MDN Web Docs, "Array.prototype.reduce()", https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

     

    이 글은 코드스테이츠 x KNU가 주최한 직무역량캠프에서 공부한 내용을 작성한 것입니다.
    Comments