[JavaScript] 일급 객체, 고차 함수, 배열 메서드(내장 고차 함수)

유진·2021년 2월 1일
0
post-thumbnail

1. 일급 객체(First-class citizen)

프로그래밍 언어에서는 유독 특별한 취급을 받는 객체가 존재한다. 프로그래밍 세계에서는 이것을 일급 객체 (first-class citizen)이라고 한다. 일급 객체는 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 말한다.

일급 객체의 조건은 다음과 같다.

  • 변수에 할당(assignment)된다
  • 함수의 인자(argument)로 넘겨진다
  • 다른 함수의 결과로 리턴된다

루비나 파이썬에서는 클래스(class)가 일급 객체(일급 클래스)이다. 자바스크립트에서는 함수(function)가 일급 객체(일급 함수)이다. 자바스크립트 뿐만 아니라 하스켈, 스칼라, 스위프트 등에서도 일급 객체로 함수를 채택했다.

사실 일급 객체의 조건에 대해서는 아직 의견이 분분하다. 위에서 서술한 조건 외에도 런타임 시 함수 생성 가능 여부를 조건으로 드는 사람도 있다. 이 경우, C언어에서의 함수는 일급 객체가 될 수 없다. C에서의 함수와 같은 객체는 경우에 따라서 이급 객체라고 부르기도 한다. 비록 일급 객체의 조건을 다 채우진 못했지만 그에 상응하는 기능을 할 때 이급 객체라 부른다.

결론적으로, 자바스크립트에서는 일급 함수를 채택했기 때문에, 함수를 변수에 담거나, 함수의 인자에 담기거나, 다른 함수의 결과로 리턴될 수 있다. 간단하게 예제를 하나씩 살펴보자.

1) 함수는 변수에 할당할 수 있다

일반적인 방식으로 함수를 선언하는 것은 함수 선언식(function declaration)이라고 하며, 함수를 변수에 할당하는 방식으로 함수를 선언하는 것을 함수 표현식(function expression)이라고 한다.

function add5(num) {  // 함수 선언식
  return num+5
}

const getSquare = function(num) { // 함수 표현식
  return num*num
}

console.log(add5(5))  // 10
console.log(getSquare(5));  // 25

함수 표현식에서 함수를 담은 변수(getSquare)를 통해 함수를 호출(getSquare())할 수 있다.

주의할 점은, 함수 표현식의 호이스팅 방식은 함수 선언식과 다르다는 것이다.

일반적으로 함수 선언식에서는 함수 전체가 호이스팅된다. 따라서 함수가 호출된 후 선언되어도 정상적으로 동작한다.

// 함수 add5가 호이스팅 된다
// function add5(num) {...}
console.log(add5(10)); 	  // 15

function add5(num) {
  return num+5;
}

반면, 함수 표현식에서는 함수를 담는 변수만 호이스팅된다. 따라서 함수를 선언하기 전에 호출하면 에러를 발생시킨다.

// 함수를 담는 변수만 호이스팅된다
// const add5;
console.log(add5(3));  // ReferenceError: Cannot access 'add5' before iniitalization

const add5 = function (num) {
    return num+5;
};

2) 함수는 함수의 인자로 넘겨진다

마치 변수처럼 함수를 함수의 인자로 넘길 수 있다. 자세한 것은 아래의 2. 고차함수에서 다룰 예정이다.

function greetMessage() {
  return 'Hi, ';
}

function sayHi(func, name){
  return func() + name;
}

console.log(sayHi(greetMessage, 'nittre'));  // Hi, nittre

3) 함수의 결과로 함수를 리턴한다

문자열, 숫자, 배열, 객체처럼 함수를 리턴할 수 있다.

function sayHi() {
  return function() {
    console.log('HI!!');
  }
}

sayHi()(); // HI!!

const greet = sayHi(); // sayHi()의 리턴값인 익명함수가 greet 변수에 담긴다
greet(); // HI!!

2. 고차 함수 (Higher order function)

고차함수는 함수를 인자로 받거나, 함수를 리턴하는 함수를 말한다. 뭔가 익숙하다면... 맞다! 일급 함수의 조건 중 2번과 3번에 같은 내용이 있다. 즉, 고차 함수는 일급 함수의 조건을 이용한 함수이다.

또한 고차 함수의 인자로 들어간 함수를 콜백 함수(callback function)라고 한다.

function greetMessage() {
  return 'Hi, ';
}

function sayHi(func, name){
  return func() + name;
}

console.log(sayHi(greetMessage, 'nittre'));  // Hi, nittre

sayHi() 함수는 인자로 func라는 함수와 name이라는 변수를 받고 있다. 그래서 sayHi() 함수를 호출할 때 첫번째 인자로 함수 greetMessage()를 넘겨주었다. 여기서 sayHi()는 함수를 인자로 받고 있으므로 고차함수이며, greetMessage는 콜백 함수가 된다.

3. 자바스크립트의 내장 고차 함수

자바스크립트의 배열 메서드 중 일부는 내장 고차 함수이다. 대표적으로는 다음의 메서드들이 있다.

  • forEach
  • find
  • filter
  • map
  • reduce
  • sort
  • some
  • every

하나씩 살펴보자

1) forEach

forEach는 배열의 모든 요소를

array.forEach(callbackFunction(currentValue, index, array), thisArg)
  • 첫번째 인자: 콜백 함수
    - currentValue: 필수. 현재 처리하고 있는 배열의 요소.
    • index: 옵션. 현재 처리하고 있는 요소의 인덱스
    • array: 옵션. forEach()를 호출한 배열
  • 두번째 인자: 옵션. 콜백함수를 실행할 때 this로 사용할 값.
// 예제
const arr = [1, 2, 3, 4, 5];

arr.forEach(el => console.log(el));

주의! forEach는 배열의 각 요소를 조회하기만 한다. 배열의 각 요소를 조작하여 새로운 배열을 만들고 싶다면 map을 사용해야 한다.

2) find

find 메서드는 콜백함수에서 true를 리턴하는 첫번째 요소를 리턴한다. 콜백함수에 true를 리턴하는 요소가 없는 경우 undefined를 리턴한다.

const arr = [1, 2, 3, 4, 5];

const val = arr.find(el => el<6);

console.log(val); // 1

요소의 값이 아니라 인덱스를 찾고 싶은 경우, findIndex() 메서드를 사용할 수 있다.

const arr = [1, 2, 3, 4, 5];
const idx = arr.findIndex(el => el===3);
console.log(idx);  // 2

3) filter

filter() 메서드는 콜백함수에서 true를 리턴하는 모든 요소들을 모아 새로운 배열로 반환한다. 콜백함수를 만족하는 요소가 없는 경우, 빈 배열을 리턴한다.

const arr = [1, 2, 3, 4, 5];

const oddNum = arr.filter(el => {
  if (el%2===1){
    return true;
  } else {
    return false
  }
};

console.log(oddNum);  // [1, 3, 5]

4) map

map() 메서드는 배열 내의 요소들을 순회한 결과를 모아 새로운 배열을 반환한다.

const arr = [1, 2, 3, 4, 5];

const squaredArr = arr.map(el => {
  return el*el
});
                           

5) reduce

누산기(accumulator)가 추가된 map() 메서드 정도로 이해할 수 있다.

arr.reduce(callback(acc, cur, idx, arr), initialValue);
  • 첫번째 인자: 콜백함수
    1. 누산기(acc)
    1. 현재 값(cur)
    2. 현재 인덱스 (옵션)
    3. 원본 배열 (옵션)
  • 두번째 인자: 초기값 (옵션)
    - 초기값이 지정되면, 누산기(acc)에는 초기값이 들어가며, 현재 값(cur)은 배열의 첫번째 요소부터 순회한다
    • 초기값이 지정되지 않으면, 누산기(acc)에는 배열의 첫번째 요소가 들어가며, 현재 값(cur)은 배열의 두번째 요소부터 순회한다.
  • 콜백함수의 리턴값은 다음 배열 요소의 누산기(acc)에 들어간다.
const arr = [1, 2, 3, 4, 5];

const sumOfArr = arr.reduce((acc, cur) => {
  return acc+cur;
});

console.log(sumOfArr); // 15

6) sort

배열의 요소를 적절한 위치에 정렬하고, 그 배열을 반환한다. 원본 배열을 수정하는 mutable 메서드이다.

arr.sort(callback(a, b));
  • 첫번째 인자: (옵션) 콜백 함수. 콜백함수가 주어지지 않은 경우, 문자열은 유니코드 코드 포인트 순서로 문자열을 비교하여 정렬한다.
    - 콜백함수에는 배열의 서로 다른 요소가 두 개 온다. (a, b)
    • 해당 콜백 함수의 리턴값이 0보다 작은 경우, a가 먼저 온다
    • 리턴값이 0이면 그대로 둔다
    • 리턴값이 0보다 크면 b가 먼저온다
/* 내림차순 정렬 예제 */
let arr = [5, 1, 3, 2, 4];

arr.sort(function (a, b) {
  return a-b;
});
console.log(arr);  // [1, 2, 3, 4, 5]

7) some

some() 메서드는 배열 안의 모든 요소를 순회하면서, 콜백 함수가 하나라도 true를 반환하면 true를 반환하고, 모두 false를 반환하면 false를 반환한다.

function isBiggerThan6(el) => {
  if (el > 6){
    return true;
  }
  else {
    return false;
  };
}

const arr1 = [1, -3, 4, 2, 8, 1];
const arr2 = [-17, 0, 3, -2];

console.log(arr1.some(isBiggerThan6));  // true
console.log(arr2.some(isBiggerThan6));  // false

주의! 빈 배열에서 호출하면 무조건 false를 반환한다.

8) every

every() 메서드는 배열 안의 모든 요소를 순회하면서 콜백 함수가 모두 true를 반환하는지 확인한다. 모든 배열 요소에 대해 콜백 함수가 true를 반환하면 true를 반환하고, 하나라도 false를 반환하면 false를 반환한다.

function isBiggerThan6(el) => {
  if (el > 6){
    return true;
  }
  else {
    return false;
  };
}

const arr1 = [8, 12, 51, 32];
const arr2 = [17, 20, 4, 10];

console.log(arr1.every(isBiggerThan6));  // true
console.log(arr2.every(isBiggerThan6));  // false

주의! 빈 배열에서 호출하면 무조건 true를 반환한다.

참고

profile
제가 또 기가막힌 한 줌의 트러플 소금 같은 존재그등요

0개의 댓글