[자바스크립트]

[JavaScript/DeepDive]27.배열(3)

ki7348 2021. 6. 7. 02:19
  • [27.9] 배열 고차 함수
  • 고차 함수는 함수를 인수로 전달받거나 함수를 반환하는 함수를 말한다.
  • 자바스크립트의 함수는 일급 객체이므로 함수를 값처럼 인수로 전달할 수 있으며 반환할 수도 있다.
  • 고차 함수는 외부 상태의 변경이나 가변 데이터를 피하고 불변성을 지향하는 함수형 프로그래밍에 기반을 두고 있다.
  • 함수형 프로그래밍은 순수 함수와 보조 함수의 조합을 통해 로직 내에 존재하는 조건문과 반복문을 제거하여 복잡성을 해결하고 변수의 사용을 억제하여 상태 변경을 피하려는 프로그래밍 패러다임이다.
  • 함수형 프로그래밍은 결국 순수 함수를 통해 부수 효과를 최대한 억제하여 오류를 피하고 프로그램의 안정성을 높이려는 노력의 일환이라고 할 수 있다.
  • Array.prototype.sort
    • sort 메서드는 배열의 요소를 정렬한다.
      • 원본 배열을 직접 변경하며 정렬된 배열을 반환한다.
        • const fruits = ['Banana', 'Orange', 'Apple'];

          // 오름차순(ascending) 정렬
          fruits.sort( );

          // sort 메서드는 원본 배열을 직접 변경한다.
          console.log(fruits); // ['Apple', 'Banana', 'Orange']
    • sort 메서드의 기본 정렬 순서는 유니코드 코드 포인트의 순서를 따른다.
      • 배열의 요소가 숫자 타입이라 할지라도 배열의 요소를 일시적으로 문자열로 변환한 후 유니코드 코드 포인트의 순서를 기준으로 정의한다.
    • 숫자 요소를 정렬할 때는 sort 메서드에 정렬 순서를 정의하는 비교 함수를 인수로 전달해야 한다.
      • const points = [40, 100, 1, 5, 2, 25, 10];

        // 숫자 배열의 오름차순 정렬. 비교 함수의 반환값이 0보다 작으면 a를 우선하여 정렬한다.
        points.sort((a, b) => a - b);
        console.log(points); // [1, 2, 5, 10, 25, 40, 100]

        // 숫자 배열에서 최소/최대값 취득
        console.log(points[0], points[points.length - 1]); // 1 100

        // 숫자 배열의 내림차순 정렬. 비교 함수의 반환값이 0보다 작으면 b를 우선하여 정렬한다.
        points.sort((a, b) => b -a);
        console.log(points); // [100, 40, 25, 10, 5, 2, 1]

        // 숫자 배열에서 최소/최대값 취득
        console.log(points[points.length - 1], points[0]); // 1 100
  • Array.prototype.forEach
    • 앞에서 살펴보았듯이 함수형 프로그래밍은 순수 함수와 보조 함수의 조합을 통해 로직 내에 존재하는 조건문과 반복문을 제거하여 복잡성을 해결하고 변수의 사용을 억제하여 상태 변경을 피하려는 프로그래밍 패러다임이다.
    • forEach 메서드는 for 문을 대체할 수 있는 고차 함수다.
      • forEach 메서드는 반복문을 추상화한 고차 함수로서 내부에서 반복문을 통해 자신을 호출한 배열을 순회하면서 수행해야 할 처리를 콜백 함수로 전달받아 반복 호출한다.
        • const numbers = [1, 2, 3];
          const pows = [ ];

          // forEach 메서드는 numbers 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출한다.
          numbers.forEach(item => pows.push(item ** 2));
          console.log(pows); // [1, 4, 9]
        • 위 예제의 경우 forEach 메서드는 numbers 배열의 모든 요소를 순회하며 콜백 함수를 반복 호출한다.
        • numbers 배열의 요소가 3개이므로 콜백 함수도 3번 호출된다.
        • 이때 콜백 함수를 호출하는 forEach 메서드는 콜백 함수에 인수를 전달할 수 있다.
    • forEach 메서드는 원본 배열(forEach 메서드를 호출한 배열, 즉 this)을 변경하지 않는다.
      • 콜백 함수를 통해 원본 배열을 변경할 수는 있다.
    • forEach 메서드의 두 번째 인수로 forEach 메서드의 콜백 함수 내부에서 this로 사용할 객체를 전달할 수 있다.
      • class Numbers {
             numberArray = [ ];
             multiply(arr) {
                  arr.forEach(function (item) {
                       this.numberArray.push(item*item);
                  }, this); // forEach 메서드의 콜백 함수 내부에서 this로 사용할 객체를 전달
             }
        }

        const numbers = new Numbers( );
        numbers.multiply([1, 2, 3]);
        console.log(numbers.numberArray); // [1, 4, 9]

      • class Numbers {
             numberArray = [ ];
             multiply(arr) {
                   // 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조한다.
                   arr.forEach(item => this.numberArray.push(item*item));
             }
        }

        const numbers = new Numbers( );
        numbers.multiply([1, 2, 3]);
        console.log(numbers.numberArray); // [1, 4, 9]
    • forEach 메서드는 for 문과는 달리 break, continue 문을 사용할 수 없다.
  • Array.prototype.map
    • map 메서드는 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출한다.
    • 그리고 콜백 함수의 반환값들로 구성된 새로운 배열을 반환한다.
    • 이때 원본 배열을 변경되지 않는다.
      • const numbers = [1, 4, 9];

        // map 메서드는 numbers 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출한다.
        // 그리고 콜백 함수의 반환값들로 구성된 새로운 배열을 반환한다.
        const roots = numbers.map(item => Math.sqrt(item));

        // 위 코드는 다음과 같다.
        // const roots = numbers.map(Math.sqrt);

        // map 메서드는 새로운 배열을 반환한다.
        console.log(roots); // [1, 2, 3]
        // map 메서드는 원본 배열을 변경하지 않는다.
        console.log(numbers); // [1, 4, 9]
    • forEach 메서드와 map 메서드의 공통점은 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출한다는 것이다.
    • 하지만 forEach 메서드는 언제나 undefined를 반환하고, map 메서드는 콜백 함수의 반환값들로 구성된 새로운 배열을 반환하는 차이가 있다.
    • map 메서드가 생성하여 반환하는 새로운 배열의 length 프로퍼티 값은 map 메서드를 호출한 배열의 length 프로퍼티 값과 반드시 일치한다.
    • 즉, map 메서드를 호출한 배열과 map 메서드가 생성하여 반환한 배열은 1:1 매핑한다.
  • Array.prototype.filter
    • filter 메서드는 자신을 호출한 배열의 모든 요소를 순회하면서 인수로 전달받은 콜백 함수를 반복 호출한다.
    • 그리고 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환한다.
    • 이때 원본 배열을 변경되지 않는다.
      • const numbers = [1, 2, 3, 4, 5];

        // filter 메서드는 numbers 배열의 모든 요소를 순회하면서 콜백 함수를 반복 호출한다.
        // 그리고 콜백 함수의 반환값이 true인 요소로만 구성된 새로운 배열을 반환한다.
        // 다음의 경우 numbers 배열에서 홀수인 요소만 필터링한다(1은 true로 평가된다).
        const odds = numbers.filter(item => item % 2);
        console.log(odds); // [1, 3, 5]
    • filter 메서드는 자신을 호출한 배열에서 필터링 조건을 만족하는 특정 요소만 추출하여 새로운 배열을 만들고 싶을 때 사용한다.
    • filter 메서드가 생성하여 반환한 새로운 배열의 length 프로퍼티 값은 filter 메서드를 호출한 배열의 length 프로퍼티 값과 같거나 작다.
  • Array.prototype.reduce
    • reduce 메서드는 자신을 호출한 배열을 모든 요소를 순회하며 인수로 전달받은 콜백 함수를 반복 호출한다.
    • 그리고 콜백 함수의 반환값을 다음 순회 시에 콜백 함수의 첫 번째 인수로 전달하면서 콜백 함수를 호출하여 하나의 결과값을 만들어 반환한다.
    • 이때 원본 배열을 변경되지 않는다.
      • // 1부터 4까지 누적을 구한다.
        const sum = [1, 2, 3, 4].reduce((accumulator, currentValue, index, array) => accumulator + currentValue, 0);

        console.log(sum); // 10
    • reduce 메서드를 호출할 때는 언제나 초기값을 전달하는 것이 안전하다.