프로그래밍-학습기록/Javascript

[Javascript] 함수 선언문 vs. 함수 표현식 feat 호이스팅

leesche 2021. 2. 17. 10:58

함수 선언문과 함수 표현식

함수 선언문(function declaration)은 function의 정의부만 존재하고 별도의 할당 명령이 없는 것을 의미합니다.

function a() { // 함수 선언문
    /* ... */
}
a(); // 정상 실행

함수명 a가 곧 변수명입니다. 함수 선언문의 경우 반드시 함수명이 정의되어 있어야 합니다.

반대로 함수 표현식(function expression)은 정의한 function을 별도의 변수에 할당하는 것을 말합니다.

var b = function () { // (익명) 함수 표현식. 변수명 b가 곧 함수명 
    /* ... */
}
b(); // 정상 실행
var c = function d () { // 기명 함수 표현식. 변수명은 c, 함수명은 d
    /* ... */
}
c(); // 정상 실행
d(); // 에러 발생

기명 함수 표현식은 외부에서 함수명으로 함수를 호출할 수 없기 때문에 주의해야 합니다. 함수명은 오직 함수 내부에서만 접근할 수 있습니다.

함수 선언문의 위험성

함수 선언문은 함수 전체가 끌어올려져 선언과 할당이 동시에 이뤄집니다. 따라서 같은 이름의 함수가 선언되어 있다면 가장 나중에 있는 함수로 덧씌워져버려 문제가 발생할 수 있습니다.

함수 선언문과 표현식의 호이스팅을 이해하기 다음 예제를 실행시켜 보겠습니다.

console.log(sum(1, 2));
console.log(multiply(3, 4));

function sum(a, b) {
  return a + b;
}

var multiply = function (a, b) {
  return a * b;
};

/* 출력 결과 
3
오류 발생 --> TypeError: multiply is not a function
*/

자바스크립트는 함수 선언문 전체를 호이스팅하는 반면 함수 표현식은 변수 선언부만 호이스팅합니다. 호이스팅을 마친 최종 상태(실제로 이렇게 되지 않지만 이해를 쉽게 하기 위한 코드)는 다음과 같습니다.

// 함수 선언문 전체를 호이스팅
var sum = function sum(a, b) {
  return a + b;
};
var multiply; // 변수는 선언부만 호이스팅

console.log(sum(1, 2));
console.log(multiply(3, 4));

// 변수의 할당부는 원래 자리에 남겨놓습니다.
multiply = function (a, b) {
  return a + b;
};

자바스크립트는 함수를 하나의 값으로 취급할 수 있습니다. 자바스크립트가 코드 실행 전 함수 선언문을 만나면, 함수 이름과 같은 변수를 선언하고 만들어 함수 선언문 전체(즉, 함수)를 할당합니다. 하지만 함수 표현식을 만나면 변수명만 끌어올려 선언합니다.

이 원칙은 나중에 치명적인 문제를 일으킬 수 있습니다.

console.log(sum(3, 4));

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

var a = sum(1, 2);

console.log(a);

function sum(x, y) {
  return x + "+" + y + " = " + (x + y);
}

var c = sum(1, 2);

console.log(c);

/* 출력 결과
3+4 = 7
1+2 = 3
1+2 = 3
*/

위 함수 선언문으로 작성된 코드는 아래와 같이 호이스팅 됩니다.

var sum = sum(x, y) {
  return x + "+" + y + " = " + (x + y);
}
var a;
var c;

console.log(sum(3, 4));

a = sum(1, 2);

console.log(a);

c = sum(1, 2);

console.log(c);

이 코드의 작성자는 sum 함수를 재정의 하여 문자열로 출력하려고 했지만, 이미 처음부터 sum 함수는 가장 아래 선언문으로 덧씌워진 상태입니다.

즉, 의도한 대로 프로그램이 실행되지 않을 수 있습니다.

하지만 함수를 함수 표현식으로 작성한다면 어떻게 될까요?

console.log(sum(3, 4));

var sum = function sum(x, y) {
  return x + y;
}

var a = sum(1, 2);

console.log(a);

var sum = function sum(x, y) {
  return x + "+" + y + " = " + (x + y);
}

var c = sum(1, 2);

console.log(c);

/* 출력 결과
3+4 = 7
1+2 = 3
1+2 = 3
*/

위 코드는 아래와 같이 호이스팅 됩니다.

// 호이스팅된 이후 코드 (실제로 이렇게 실행되는 것은 아니지만, 해석을 위해)

var sum;
var a;
var c;

console.log(sum(3, 4)); // TypeError: sum is not a function

sum = function sum(x, y) {
  return x + y;
};

a = sum(1, 2);

console.log(a);

sum = function sum(x, y) {
  return x + "+" + y + " = " + (x + y);
};

c = sum(1, 2);

console.log(c);

결론

함수 표현식이 안전하다. 함수 표현식 쓰자.

출처 및 참고 문헌

  • 책 <코어 자바스크립트>