자바스크립트는 함수를 정의하는 데에 있어서 두 가지 기본적인 방법을 제공합니다. 함수 표현식과 함수 선언문이죠. 이 두 가지 방식은 각각의 특징과 장단점이 있으며, 코드를 작성할 때 어떤 것을 선택해야 할지 고민이 될 수 있습니다. 저 또한 이전까지 막연히 함수 표현식을 사용하는 것이 좋다고 생각했었는데, 리액트 공식문서 등 꽤 많은 곳에서 왜 함수 선언문을 사용하는 지에 대해 궁금증이 생겨 알아보게 되었습니다. 이번 글에서는 함수 표현식과 함수 선언문의 차이를 알아보고, 어떤 방식을 사용하는 것이 더 나을지에 대해 살펴보겠습니다.

함수 선언문 - Function Declarations

함수 선언문은 함수를 선언하면서 동시에 정의합니다. 함수 이름이 필수적이며, 함수 선언문은 호이스팅이 발생하여 어디에서든지 함수를 호출할 수 있습니다. 

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

함수 표현식 - Function Expressions

함수 표현식은 변수에 함수를 할당하는 방식으로 정의됩니다. 함수는 익명으로 정의될 수 있고, 변수에 할당된 함수를 통해 호출됩니다.

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

 

 

차이점

호이스팅

자바스크립트는 스크립트를 실행하기 전, 준비단계에서 전역에 선언된 함수 선언문을 찾고, 해당 함수를 생성합니다. 스크립트가 진짜 실행되기 전 "초기화 단계"에서 함수 선언 방식으로 정의한 함수가 생성되는 것이죠. 따라서 스크립트 어디서든 함수 선언문으로 선언한 함수에 접근할 수 있는 것입니다. 이를 함수 호이스팅이라고 합니다.

myFn(); 
// 선언 전에 호출되도 정상 동작

function myFn() { console.log("동작") }

반면 함수 표현식은 함수 표현식으로 정의한 함수는 함수가 선언되기 전에 접근하는 게 불가능합니다. 이때 변수에 함수가 할당되기 이전이라면, 자바스크립트는 해당 변수를 함수인 것을 모르기 때문에 변수로 취급되며, 호이스팅의 영향을 받습니다. 따라서 Uncaught ReferenceError: myFn is not defined(const혹은 let) 혹은 Uncaught TypeError: myFn is not a function(var 사용) 과 같은 오류를 볼 수 있습니다.

add(); // Uncaught ReferenceError: myFn is not defined

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

subtract(); // Uncaught TypeError: myFn is not a function

var subtract = function(a,b) {
	return a - b;
}

스코프

자바스크립트의 strict mode를 사용할 때, 함수 선언문이 코드 블록 내에 위치하면 해당 함수는 블록 내 어디서든 접근할 수 있습니다. 하지만 블록 밖에서는 함수에 접근하지 못합니다.

strict mode
JavaScript 코드를 더 안전하고 예측 가능하게 만들기 위해 도입된 특별한 실행 모드입니다.
엄격 모드에서는 변수와 함수의 스코프를 명확하게 구분하여 예기치 않은 동작을 방지하고 안전한 코드를 작성할 수 있도록 돕습니다.

 

런타임 시에 그 값을 알 수 있는 변수를 사용하여 함수를 정의해야 될 때, 다음과 같은 문제점이 발생할 수 있습니다.

"use strict";
// 조건에 따라 함수를 선언
const someValue = prompt("값 입력", 1);
if (someValue === 1) {
  myFn(); // 출력: "런타임시에 someValue의 값은 1이다."

  function myFn() {
    console.log("런타임시에 someValue의 값은 1이다.");
  }

} else {
  myFn(); // 출력: "런타임시에 someValue의 값은 1이 아니다."

  function myFn() {
    console.log("런타임시에 someValue의 값은 1이 아니다.");
  }

}

myFn(); // Error: myFn is not defined

조건문 안에서 선언문으로 함수를 선언하고 난 뒤 바로 호출할 수도 있지만, 만약 조건문 밖에서  함수를 호출하려고 하면 에러가 발생합니다. 이럴때는 함수 표현식으로 다음과 같이 사용할 수 있습니다. (삼항조건 연산자로 코드를 더욱 단순하게 만들 수도 있습니다.)

"use strict"

let someValue = prompt("값 입력", 1);

let myFn = (someValue === 1) ?
  function() { console.log("런타임시에 someValue의 값은 1이다."); } :
  function() { console.log("런타임시에 someValue의 값은 1이 아니다."); };

myFn();

그렇다면 함수 표현식을 쓰는 것이 나은가?

많은 개발자들이 일반적으로는 함수 선언문을 사용하되, 어떤 이유로 함수 선언 방식이 적합하지 않은 경우에 함수 표현식을 사용하는 것을 추천하는 것으로 보입니다.

 

만약 함수 선언문으로 함수를 정의하면, 함수가 선언되기 전에 호출할 수 있어서 코드 구성을 좀 더 자유롭게 할 수 있습니다. 코드의 순서를 개발자가 원하는 순서, 즉 논리에 따라 사람이 읽기 쉬운 순서로 작성할 수 있게 됩니다. 더 읽기 쉬운 코드가 되죠. 함수 선언문을 사용하면 가독성도 좋아집니다. 코드에서 let fn = function(…) {…}보다 function fn(…) {…} 을 찾는 게 더 쉽죠.

참고 자료

모던 자바스크림트 튜토리얼, 함수 표현식

+ Recent posts