자바스크립트

Section 4. 자바스크립트 일급 객체

포칼이 2023. 3. 26. 20:59

일급 객체란?

  • 함수를 변수와 같이 다루는 언어에 있는 개념이다. 
  • 자바스크립트의 함수도 일급 객체이다. 함수는 기본적으로 객체이다.
// ⭐️ 함수의 자료형
function addNumbers (numberA, numberB) { return numberA + numberB; }
console.log(typeof addNumbers); //function

함수 addNumbers의 자료형은 function임을 알 수 있다. 

 

일급 객체의 특성은 다음과 같다

  1. 상수 또는 변수에 할당될 수 있다.
  2. 다른 함수에 인자로 전달될 수 있다.
  3. 다른 함수의 결과값으로서 반환될 수 있다.

I.할당

function isOddNum (number) {
  console.log(
    (number % 2 ? '홀' : '짝')
    + '수입니다.'
  );
  return number % 2 ? true : false;
};

const checkIfOdd = isOddNum; // 뒤에 괄호 없음 유의

console.log(checkIfOdd(23)); //홀수입니다. 
			     //true

단순히 number가 홀수이면 false를 반환하고 짝수이면 true를 반환하는 함수 isOddNum이 있다. 

이 함수를 checkIfOdd에 그냥 할당을 해버릴 수 있다는 것이다.

const checkIfOdd = isOddNum(5); 이런식으로 하면 isOddNum에 5를 넣어서 실행한 값을 반환하게 되는 것인데 예시에는 받는 인자를 생략한 것을 볼 수 있다. 이런식으로 함수 자체를 변수에 할당 할 수 있다는 것이다. 

 

let x = 7, y = 3;

let func1 = (functionInputValue1, functionInputValue2) => functionInputValue1 + functionInputValue2;
let func2 = (functionInputValue1, functionInputValue2) => functionInputValue1 - functionInputValue2;
console.log(func1(x, y), func2(x, y)); //10 4

func1 = func2
console.log(func1(x, y), func2(x, y)); //4 4

여기서 중요한 점은 함수도 참조타입이라는 점이다. 

 

객체와 배열의 값으로도 할당 가능하다

let person = {
  name: '홍길동',
  age: 30,
  married: true,
  introduce: function (formal) {
    return formal
    ? '안녕하십니까. 홍길동 대리라고 합니다.'
    : '안녕하세요, 홍길동이라고 해요.';
  }
};

console.log(person.introduce(true)); //안녕하십니까. 홍길동 대리라고 합니다.
console.log(person.introduce(false)); //안녕하세요, 홍길동이라고 해요.

 

 다만 배열에 함수를 넣을 때는 introduce라는 프로퍼티 key를 사용한다. function다음에 이름이 없는데 그것을 introduce가 대신하는 것이다. 

 

객체에 함수 프로퍼티를 포함할 때 기억할 것

let person = {
  name: '홍길동',
  age: 30,
  married: true,
  introduce: function () {
    return `저는 ${this.name}, ${this.age}살이고 `
    + `${this.married ? '기혼' : '미혼'}입니다.`;
  }
}

console.log(person.introduce()); //저는 홍길동, 30살이고 기혼입니다.

여기서 this는 무엇일까?

바로 person이란 객체 자체를 의미한다. 그래서 this.name 하면 person의 객체의 name이 되는 것이다. 

즉, 객체의 다른 프로퍼티에 접근하려면 this를 사용하면 된다.

*introduce에 화살표 함수를 사용하면 this.name처럼 접근을 해서 사용할 수 없다. 지금은 이렇게만 외우고 넘어가자.

 

II. 인자로 전달

  • 함수가 다른 함수를 인자로 전달받는다.
  • 전달받는 함수는 고차 함수이다.
  • 전달되는 함수는 콜백 함수이다. 
let list = [1, 2, 3, 4, 5];

function doInArray (array, func) {
  for (item of array) {
    func(item);
  }
}

// console.log - console이란 객체에서 log란 키에 할당된 함수
doInArray(list, console.log);

여기서 고차 함수란 바깥쪽에 있는 전달을 받는 함수 즉, doInArray가 되고

콜백 함수란 다른 함수 안에 전달되서 들어가는 함수 즉, console.log가 된다. 

풀어서 설명을 하면 다음과 같다.

function doInArray는 인자로 array와 func을 받는다. 여기서 array는 배열, func는 함수를 말한다. doInArray 함수 안에서 item이라는 곳에 array의 요소들이 하나씩 차례대로 담기게 된다. 그리고 func이라는 함수에 item이 인자로 들어가서 func가 실행되는 것이다. 

doInArray(list, console.log) 를 하면 array에 list라는 배열이 들어가게 되고 console.log라는 함수가 func에 들어가게 되는 것이다. 따라서 list의 배열이 item에 차례로 담기고 그것이 func함수 즉, console.log라는 함수에 item이 인자로 들어가서 실행이 되는 것이다. 출력은 다음과 같다.

1

2

3

4

또 다른 예시를 보자

function doNTimes (func, repeat, x, y) {
  let result = x;
  for (i = 0; i < repeat; i++) {
    result = func(result, y);
  }
  return result;
}

console.log(
  doNTimes((x, y) => x * y, 3, 5, 2), //40
  doNTimes((x, y) => x / y, 3, 5, 2), //0.625
);

doNTimes 의 func 는 (x, y) => x * y 즉, x와 y라는 인자를 받아서 그걸 곱한 값을 반환하는 함수이다. 

그리고 repeat에는 3

x와 y에는 각각 5와 2가 대입된다고 할 수 있다. 

result = 5이고 for문은 i=0, 1, 2까지 총 3번 반복된다.

for문 안에서 result는 i= 0 일때 10, i= 1일때 20, i = 2일때 40이 되고 for문을 나와 result 값 40이 반환이 되고 함수를 종료하게 된다. 

*doNTimes안의 화살표 함수처럼 이름이 없는 함수를 익명함수라고 한다. 

익명함수는 인자로 전달된 함수들이고 변수나 상수에 할당되지 않아 이름이 없는 함수를 말한다. 

 

III. 결과값으로 반환

function getIntroFunc (name, formal) {
  return formal
  ? function () {
    console.log(`안녕하십니까, ${name}입니다.`);
  } : function () {
    console.log(`안녕하세요~ ${name}이라고 해요.`);
  }
}

const hongIntro = getIntroFunc('홍길동', true);
const jeonIntro = getIntroFunc('전우치', false);

hongIntro(); //안녕하십니까, 홍길동입니다.
jeonIntro(); //안녕하세요~ 전우치이라고 해요.

반환하는 값이 함수임을 볼 수 있다. 

변수 hongIntro에 함수 getIntroFuc('홍길동', true)를 실행한 값이 담긴다. 근데 그 값이 function()이란 익명 함수인 것이다.

그래서 hongIntro(); 이렇게 호출하면 function ()가 호출되어 출력된다. 

*function()은 인자를 받지 않아서 hongIntro()로 호출한다. 

 

또 다른 예시를 보자.

const add = (a, b) => a + b;
const sub = (a, b) => a - b;
const mul = (a, b) => a * b;
const div = (a, b) => a / b;

function comb3ArmFuncs(armFunc1, armFunc2, armFunc3) {
  return (x, y) => armFunc3(armFunc2(armFunc1(x, y), y), y);
}

const add_mul_sub = comb3ArmFuncs(add, mul, sub);
const mul_add_div = comb3ArmFuncs(mul, add, div);
const div_add_mul = comb3ArmFuncs(div, add, mul);

console.log(
  add_mul_sub(10, 4), //52
  mul_add_div(10, 4), //11
  div_add_mul(10, 4) //26
);

사칙연산을 하는 함수 add, sub, mul, div가 있다. 근데 comb3ArmFuncs함수는 인자로 함수를 3개를 받고 그 return 값을 또 다른 익명함수로 받는다. 그 익명함수는 armFunc1(x, y)를 실행한 값으로 armFunc2(전에 함수 실행한 값 , y)를 실행한 값으로 armFunc3(전에 함수 실행한 값, y) 를 실행하는 함수이다. 

중요한 점은 함수들이 누적되어 실행될 때 그 순서는 항상 안쪽에서 바깥쪽이라는 점이다. 

나머지는 단순 계산이므로 굳이 적지는 않겠다. 

 

이 포스트를 마치며...

하나의 함수는 한 가지 일만 하도록 작성하는 것이 좋다.

여러가지 일을 해야 한다면 각자 하나의 일을 하는 여러 함수들의 조합을 사용할 것을 권장한다.