JS 함수

정현승·2024년 10월 24일

함수

함수는 하나의 특별한 목적을 가지고 코드를 실행하도록 만드는 문법입니다.

자바스크립트는 함수 표현식(function expression)과 함수 선언식(function Declarations)으로 구분합니다.

문법

함수선언식

function func(){}

함수표현식

let func = function(){}; //unnamed function 
let func2 = function sum(){}; //named function

과거에는 디버깅을 쉽게 하기위해 네이밍 함수 많이 사용했습니다.
하지만 최신 도구와 기능의 발전으로 익명 함수 사용이 더 많아졌으며, 디버깅도 이전보다 훨씬 수월해졌습니다.

화살표함수

const func = () => {};

화살표 함수에서 중괄호를 생략하는 경우는 한 줄로 표현되는 간단한 함수일 때 가능하며, 암시적 반환(implicit return)이라는 개념을 사용합니다.
이 경우 return 키워드를 명시하지 않아도, 그 줄의 표현식이 자동으로 반환됩니다.
화살표 함수에서 중괄호 생략 규칙
중괄호를 생략하면 함수 본문에 있는 표현식의 결과가 자동으로 반환됩니다. 이때 return을 사용하지 않아도 됩니다.
중괄호를 사용하는 경우에는 return을 명시해야 값이 반환됩니다.

중괄호 생략(암시적 반환)

const add = (a, b) => a + b;

console.log(add(2, 3));  // 출력: 5

이 경우 a + b 표현식이 자동으로 반환됩니다. 중괄호가 없으므로 return 키워드를 명시할 필요가 없습니다.

중괄호 사용 (명시적 반환)

const add = (a, b) => {
  return a + b;
};

console.log(add(2, 3));  // 출력: 5

여기서는 중괄호를 사용했기 때문에, return 키워드를 명시적으로 사용해야 값이 반환됩니다.

객체를 반환하는 경우

const getUser = () => ({ name: 'Alice', age: 25 });

console.log(getUser());  // 출력: { name: 'Alice', age: 25 }

만약 객체를 반환해야 하는데 중괄호를 생략한다면, 객체 리터럴을 괄호로 감싸주어야 합니다.
그렇지 않으면 중괄호가 함수 블록으로 인식됩니다.

호출

함수는 함수의 이름과 소괄호 ( ) 를 이용해서 호출합니다.

function sum(){}; 
sum(); // 호출

함수의 매개변수

네이밍된 매개변수

일반적인 함수는 미리 정의된 매개변수를 받을 수 있습니다.
이 경우 함수 호출 시 인수를 해당 매개변수에 전달하여 사용합니다.

function sum(a, b){
  console.log(a+b);
}
sum(10,20);

매개변수에 기본값 사용하기
기본값은 함수 선언 시 매개변수에 할당할 수 있습니다. 기본값이 지정된 매개변수는 전달된 값이 undefined일 때만 해당 기본값을 사용합니다.
매개변수의 기본값은 오른쪽에 위치한 매개변수부터 사용하는 것이 좋습니다. 그렇지 않으면, 값을 전달할 때 헷갈릴 수 있습니다.

function sum(a, b=10){
  console.log(a+b);
}
sum(10);

기본값은 단순한 값뿐만 아니라 함수 호출이나 표현식도 사용할 수 있습니다.

function add(a = 1, b = a * 2) {
  return a + b;
}

console.log(add());  // 출력: 3 (a=1, b=2)
console.log(add(3));  // 출력: 9 (a=3, b=6)

나머지 매개변수 (Rest Parameters - ES6)

나머지 매개변수는 ES6에서 도입된 기능으로, 함수의 매개변수로 전달되는 불특정 다수의 인수를 배열 형태로 받을 수 있습니다.
인수의 수를 사전에 알 수 없을 때 유용합니다.

function sum(...args){
  console.log(args[0] + args[1]);
}
sum(10,20);
  • ...args는 배열로 전달된 모든 인수를 의미합니다.
  • args[0], args[1] 등으로 배열의 원소를 참조할 수 있습니다.
  • 인자의 개수가 고정되지 않아, 어떤 개수의 인자도 처리할 수 있습니다

가변인자 (Arguments 객체)

arguments 객체는 함수가 호출될 때 전달된 모든 인수를 포함하는 유사 배열 객체입니다.
ES6 이전에는 주로 가변 인자를 처리하기 위해 사용되었으며, 현재도 여전히 사용 가능합니다.
하지만 arguments는 배열이 아니라 유사 배열 객체이기 때문에 배열 메서드를 직접 사용할 수 없고, 성능이나 기능 면에서 나머지 매개변수(...)에 비해 불편할 수 있습니다.
화살표 함수에서는 사용 불가능합니다.

function sum(){
  for(let value of arguments){
	sum+=value
  }
}
sum(10,20);
  • arguments는 함수 호출 시 전달된 모든 인수에 접근할 수 있습니다.
  • 배열과 비슷하지만 배열은 아니므로 map이나 reduce 같은 배열 메서드를 직접 사용하려면 변환이 필요합니다.
  • 화살표 함수에서는 arguments를 사용할 수 없습니다.

arguments와 나머지 매개변수 비교
arguments는 함수 내부에서 자동으로 생성되지만, 유사 배열 객체입니다.
나머지 매개변수는 진짜 배열로 반환되며, 배열 메서드를 직접 사용할 수 있어 더 유연합니다.

return

return의 주요 역할

함수에서 return은 매우 중요한 역할을 하며, 함수가 값을 호출한 곳으로 반환하도록 합니다.
return이 없을 경우 함수는 기본적으로 undefined를 반환합니다.
이를 통해 함수가 수행한 연산의 결과나 값을 다른 곳에서 사용할 수 있습니다.

  • 값 반환: 함수를 호출한 코드로 값을 반환합니다.
function add(a, b) {
  return a + b;  // a와 b의 합을 반환
}

const result = add(5, 3);
console.log(result);  // 출력: 8
  • 함수 종료: return을 만나면 함수는 즉시 종료됩니다. 이후의 코드는 실행되지 않습니다.
function checkPositive(number) {
  if (number > 0) {
    return "Positive";
  }
  return "Not Positive";
}

console.log(checkPositive(10));  // 출력: Positive
console.log(checkPositive(-5));  // 출력: Not Positive
  • 반환값을 활용한 로직 연결: 반환값을 다른 함수나 로직에서 계속 사용할 수 있어, 보다 복잡한 작업이나 연산을 체인 형태로 처리할 수 있습니다.
function multiply(a, b) {
  return a * b;
}

function square(num) {
  return multiply(num, num);
}

const squaredValue = square(4);
console.log(squaredValue);  // 출력: 16

return이 없는 경우

return을 명시하지 않으면, 함수는 undefined를 반환합니다. 즉, 함수는 여전히 실행되지만, 반환되는 값은 없습니다.

function sayHello() {
  console.log("Hello!");
}

const result = sayHello();  // 출력: Hello!
console.log(result);  // 출력: undefined

재귀함수

재귀 함수는 자기 자신을 다시 호출하는 함수를 의미합니다.
즉, 함수가 자신의 정의 안에서 자신을 호출하여 문제를 반복적으로 해결해 나가는 방식입니다.
재귀 함수는 주로 복잡한 문제를 더 작은 하위 문제로 나눠서 해결할 때 유용합니다.

재귀 함수의 기본 구조

재귀 함수는 두 가지 중요한 요소로 구성됩니다:

  • 기본 사례 (Base Case): 재귀 호출을 멈추기 위한 조건. 이 조건이 충족되면 더 이상 함수가 자기 자신을 호출하지 않고 종료됩니다.
  • 재귀 사례 (Recursive Case): 기본 사례에 도달할 때까지 함수를 계속해서 다시 호출하는 부분.

예시: 팩토리얼 계산

팩토리얼은 수학에서 주어진 양의 정수 n에 대해, 1부터 n까지의 모든 수를 곱한 결과를 말합니다.
이를 재귀적으로 계산할 수 있습니다.

function factorial(n) {
  if (n === 1) {  // 기본 사례: n이 1이면 종료
    return 1;
  }
  return n * factorial(n - 1);  // 재귀 호출: n이 1이 될 때까지 계속 호출
}

console.log(factorial(5));  // 출력: 120 (5 * 4 * 3 * 2 * 1)
  1. factorial(5)가 호출되면, n은 5이므로 재귀 호출을 진행하여 factorial(4)를 호출합니다.
  2. factorial(4)는 다시 factorial(3)을 호출하는 방식으로 계속 진행됩니다.
  3. n === 1에 도달하면, 함수는 더 이상 재귀 호출을 하지 않고 값을 반환하기 시작합니다.
  4. factorial(1)이 1을 반환하고, 그 값이 다시 위로 전달되어 최종적으로 factorial(5)까지 계산됩니다.

예시: 피보나치 수열

피보나치 수열은 첫 번째와 두 번째 숫자가 1이고, 이후의 숫자가 앞의 두 숫자의 합으로 이루어진 수열입니다.
피보나치 수열도 재귀적으로 계산할 수 있습니다.

function fibonacci(n) {
  if (n === 1 || n === 2) {  // 기본 사례: 첫 번째나 두 번째 피보나치 수는 1
    return 1;
  }
  return fibonacci(n - 1) + fibonacci(n - 2);  // 재귀 호출
}

console.log(fibonacci(6));  // 출력: 8 (1, 1, 2, 3, 5, 8)

재귀 함수의 장점

  • 복잡한 문제를 작은 하위 문제로 나눠서 해결할 수 있습니다.
  • 트리 구조를 탐색하거나 분할 정복 알고리즘에서 유용합니다.
  • 반복적인 작업을 코드의 흐름을 명확하게 유지하면서 처리할 수 있습니다.

재귀 함수의 단점

  • 잘못 작성하면 무한 재귀에 빠질 수 있습니다. 즉, 기본 사례 없이 계속해서 자기 자신을 호출하면 스택 오버플로우 오류가 발생합니다.
  • 반복적으로 함수를 호출하면서 스택에 함수 호출 정보가 쌓이기 때문에, 재귀가 너무 깊어지면 메모리 사용량이 많아집니다.

재귀 함수의 효율성

재귀 함수는 간결한 코드를 작성하는 데 유용하지만, 성능이 문제일 수 있습니다.
위 피보나치 수열의 예시처럼 재귀 호출이 많아지면 동일한 계산이 반복적으로 이루어져 비효율적입니다.
이를 개선하기 위해 메모이제이션 같은 기법을 사용할 수 있습니다.

0개의 댓글