[자바스크립트 Deep Dive] 12. 함수

unhyif·2022년 5월 29일
0

12.1 함수란?

코드 블록으로 감싸져 있는, 문으로 이루어진 하나의 실행 단위

  • 매개변수(parameter): 함수 내부로 입력을 전달받는 변수 - 아래 예시의 x,y
  • 인수(argument): 함수 호출 시 입력값 - 아래 예시의 2,5
  • 반환값(return value): 함수의 출력값 - 아래 예시의 x+y
function add(x,y) {
  return x + y;
}

add(2,5)

12.2 함수를 사용하는 이유

  1. 동일한 작업을 여러 번 수행해야 할 때, 코드를 재사용함으로써 코드의 중복을 줄일 수 있음
  2. 유지보수에 용이해짐

12.3 함수 리터럴

  • 함수 이름, 매개변수 목록, 함수 몸체를 통해 함수 객체를 생성함
  • JS 함수는 일급 객체로, 값처럼 사용할 수 있음

일급 객체: 값의 성질을 갖는 객체


12.4 함수 정의

12.4.1 함수 선언문

별도의 할당 명령 없이, 함수 이름을 포함하는 함수 리터럴로 함수를 생성하는 방식

  • 이때의 함수 리터럴은 표현식이 아닌 문임

  • JS 엔진은 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고, 생성된 함수 객체를 할당함
    -> 즉, 함수 이름이 아니라 함수 객체를 가리키는 식별자로 함수를 호출하는 것임
    -> 결론적으로, JS 엔진은 함수 선언문을 함수 표현식으로 변환하여 함수 객체를 생성함 (but 함수 선언문 ≠ 함수 표현식)

function add(x, y) {
  return x + y;
}

console.log(add(2, 5)); // 7

12.4.2. 함수 표현식

함수 리터럴로 생성한 함수를 변수에 할당하는 방식

  • 이때의 함수 리터럴은 표현식인 문임
  • 보통 함수 이름을 생략함
const add = function (x, y) {
  return x + y;
};

console.log(add(2, 5)); // 7

12.4.3 함수 생성 시점과 함수 호이스팅

  • 함수 선언문으로 정의한 함수와, 함수 표현식으로 정의한 함수의 생성 시점이 다름

함수 선언문

  • 함수 호이스팅으로 인해 함수 선언문 이전에 함수를 호출할 수 있음

함수 호이스팅: 런타임 이전에 함수 객체가 생성되고, 함수 이름과 동일한 이름의 식별자가 해당 객체로 초기화됨

함수 표현식

  • 변수 호이스팅으로 인해 변수 선언문 이전에 함수를 호출할 수 없음
  • 런타임에 할당문이 실행될 때 함수 객체가 생성됨

변수 호이스팅: var 키워드로 선언됐을 경우, 런타임 이전에 식별자가 undefined로 초기화됨

12.4.4 Function 생성자 함수

  • 클로저를 생성하지 않음
const add = Function("x", "y", "return x+y");

console.log(add(2, 5)); // 7

cf) 생성자 함수: 객체를 생성하는 함수

12.4.5 화살표 함수

  • 생성자 함수로 사용할 수 없음
  • 기존 함수와 this 바인딩 방식이 다름
  • prototype 프로퍼티 X
  • arguments 객체 생성 X
const add = (x, y) => x + y;

console.log(add(2, 5)); // 7

12.5 함수 호출

12.5.1 매개변수와 인수

  • 매개변수의 스코프: 함수 내부
  • 인수가 할당되지 않은 매개변수의 값은 undefined
function add(x, y) {
  console.log(x, y);
}

add(2); // 2 undefined
  • 매개변수보다 인수가 더 많은 경우, 초과된 인수는 무시됨
    but 모든 인수는 arguments 객체에 보관됨
function add(x, y) {
  console.log(x, y);
  console.log(arguments);
}

add(2, 3, 4); // 2 3
// Arguments(3) [2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]

12.5.2 인수 확인

  • JS는 동적 타입 언어로, 매개 변수의 타입을 사전에 지정할 수 없음
    -> 함수 내부에서 인수의 타입을 확인하거나, 타입스크립트 등 JS의 상위 확장을 사용할 수 있음
function add(x, y) {
  if (typeof x !== "number" || typeof y !== "number")
    throw new TypeError("인수는 모두 숫자 값이어야 합니다.");

  return x + y;
}

console.log(add(2)); // Uncaught TypeError: 인수는 모두 숫자 값이어야 합니다.
  • JS 함수는 매개변수와 인수의 개수가 서로 일치하는지 확인하지 않음
    -> 매개변수에 기본값을 설정할 수 있음
function add(x = 0, y = 0) {
  return x + y;
}

console.log(add(1)); // 1

12.5.3 매개변수의 최대 개수

  • 매개변수는 최대한 적을수록 좋고, 많이 필요할 경우 하나의 매개변수에 객체를 인수로 전달하는 것이 좋음
    • but 전달된 객체를 함수 내부에서 변경한다면, 함수 외부의 객체도 변경되는 부수 효과가 발생함
function call(obj) {
  console.log(obj.data.id); // 1
}

call({
  method: "POST",
  url: "/user",
  data: { id: 1, name: "J" },
  cache: false,
});

12.5.4 반환문

  • 함수 실행을 중단함
  • return 키워드 뒤의 표현식을 평가하여 반환함
    • 표현식을 지정하지 않거나, 반환문이 없으면 undefined를 반환함
function multiply(x, y) {
  return x * y;
  console.log("실행 X");
}

console.log(multiply(2, 3)); // 6
function foo() {}

console.log(foo()); // undefined

12.6 참조에 의한 전달과 외부 상태의 변경

  • 원시 타입 인수는 값 자체가 복사되어 매개변수에 전달되므로, 함수 내에서 값을 변경해도 원본은 변경되지 않음
  • 객체 타입 인수는 참조값이 복사되어 매개변수에 전달되므로, 함수 내에서 참조값을 통해 객체를 변경하면 원본 또한 변경됨
    -> 원본 객체를 불변 객체(by Object.freeze())로 만들거나, 전달받은 객체를 방어적 복사(by Object.assign() or spread operator)하여 내용을 변경함으로써 외부 상태가 변경되는 부수 효과를 방지함
function changeVal(primitive, obj) {
  primitive += 100;
  obj.name = "H";
}

let num = 100;
obj = { name: "J" };
console.log(num, obj); // 100 {name: 'J'}

changeVal(num, obj);
console.log(num, obj); // 100 {name: 'H'}

12.7 다양한 함수의 형태

12.7.1 즉시 실행 함수

  • IIFE: 함수 정의와 동시에 호출되는 함수로, 단 한 번만 호출됨
const res = (function (a, b) {
  return a * b;
})(3, 5);

console.log(res); // 15

12.7.2 재귀 함수

  • 자기 자신을 호출하는 행위를 포함하는 함수
  • 반복되는 처리를 반복문 없이 구현할 수 있음
  • 재귀 호출을 멈출 수 있는 탈출 조건이 반드시 필요함
function countdown(n) {
  if (!n) return;
  console.log(n);
  countdown(n - 1);
}

countdown(5); // 5 4 3 2 1
function factorial(n) {
  if (n <= 1) return 1;
  return n * factorial(n - 1);
}

console.log(factorial(5)); // 120

12.7.3 중첩 함수

  • 함수 내부에 정의된 함수

cf) 외부 함수: 중첩 함수를 포함하는 함수

function outer() {
  const x = 1;

  function inner() {
    const y = 2;
    console.log(x + y);
  }

  inner();
}

outer(); // 3

12.7.4 콜백 함수

매개변수를 통해 다른 함수의 내부로 전달되는 함수

고차 함수

매개변수를 통해 외부에서 콜백 함수를 전달받는 함수

-> 콜백 함수는 고차 함수에 의해 호출됨

function foo(cb) {
  cb();
}

foo(function () {
  console.log(1);
}); // 고차 함수가 호출될 때마다 콜백 함수가 생성됨
function foo(cb) {
  cb();
}

// 콜백 함수는 한 번만 생성됨
const cb = function () {
  console.log(1);
};

foo(cb);

12.7.5 순수 함수와 비순수 함수

순수 함수: 외부 상태를 변경하지 않고, 외부 상태에 의존하지도 않는 함수

-> 동일한 인수가 전달되면 언제나 동일한 값을 반환함

  • 일반적으로 최소 하나 이상의 인수를 전달받음
  • 인수의 불변성을 유지함

함수가 외부 상태를 변경하면 상태 변화를 추적하기 어려워지므로, 순수 함수를 사용하는 것이 좋음

let count = 0;

// 순수 함수
function increase(n) {
  return ++n;
}

// 비순수 함수
function increase() {
  return ++count;
}

cf) 함수형 프로그래밍: 순수 함수를 통해 부수 효과를 최대한 억제하여 프로그램의 안정성을 높이려는 프로그래밍 패러다임

0개의 댓글