JS에서는 함수 선언문보다는 함수 표현식을 더 권장한다. 그 이유는 무엇일까?

먼저 함수 선언문과 함수 표현식에 대해 알아보자

// 함수 선언문
function a () {
	console.log("hello world");
}


// 익명 함수 표현식
var b = function () {
	console.log("hello world");
}

Javascript라는 언어는 코드가 실행할 때 ‘호이스팅’이 발생한다.

호이스팅을 하게 되면 함수 선언문은 전체를 호이스팅을 하게되고 함수 표현식은 선언부만 끌어 올리게 된다.

함수 선언문과 함수 표현식

  • 코드 예시

    // 함수 선언문, 함수명 a가 곧 변수명
    function a() { /* .... */ }
    a(); // 실행 OK
    
    // 익명 함수 표현식, 변수 b가 곧 함수명
    var b = function () { /* ... */ }
    b(); // 실행 OK
    
    // 기명 함수 표현식, 변수명은 c 함수명은 d
    var c = function d() { /* ... */ }
    c(); // 실행 OK
    d(); // 실행 NO
    

    🆙 function d() 가 실행이 안되는 이유는?

      외부에서는 함수명을 함수를 호출할 수 없다. 현재 함수 d는 함수 c안에 있다.
      과거에는 기명 함수 표현식은 함수명이 잘 출력 됐던 반면 익명함수 표현식은 undefined 또는 unnamed라는 값이 나왔었다. 
      이 때문에 기명 함수 표현식이 디버깅 시 어떤 함수인지를 추적하기에 익명 함수 표현식보다 유리한 측면이 있었다.
      그러나 이제는 모든 브라우저들이 익명 함수 표현식의 변수명을 함수의 name 프로퍼티에 할당하고 있다.
    
      한편 c() 함수 내부에서 c()로 호출하든 d()로 호출하든 잘 실행 된다. 
      따라서 함수 내부에서 재귀함수를 호출하는 용도로 함수명을 쓸 수 있다.
    
      다만 c()로 호출해도 되는 상황에서 굳이 d()로 호출해야할 필요가 있을지는 의문이다.
    

함수 선언문과 함수 표현식의 실질적인 차이 😇

  • 원본코드

    console.log(sum(1, 2));
    console.log(multiply(3, 4));
    
    // 함수 선언문 sum
    function sum (a, b) {
    	return a + b ;
    }
    
    // 익명 함수 표현식 multiply
    var multiply = function (a, b) {
        return a + b;
    }
    

    실행컨텍스트의 렉시컬 환경은 두 가지 정보를 수집하는데, 여기서는 그중에서 ER의 정보 수집 과정에서 발생하는 호이스팅을 살펴보자

  • 호이스팅을 다 마친 상태

    // 함수 선언문은 전체를 호이스팅 한다.
    function sum(a, b) {
        return a + b;
    }
    
    // 변수는 선언부만 끌어올린다.
    var multiply;
    
    console.log(sum(1, 2)); // OK
    console.log(multiply(3, 4)); // NO ❌
    
    // 변수의 할당부는 원래 자리에 남게된다.
    multiply = function (a, b) {
        return a + b;
    }
    

    함수도 하나의 값으로 취급할 수 있다는 것이 바로 이런 것이다. 함수를 다른 변수에 값으로써 할당한 것이 곧 함수 표현식이다.

  • 에러메세지

    TypeError: multiply is not a function
    

하지만 우리는 왜 함수 표현식이 이렇게 에러가 발생함에도 불구하고 왜 함수 표현식을 더 권장하는걸까? 🧐🧐

얼핏 보면 함수 선언식이 에러도 나지 않고 선언하기 전에도 사용할 수 있기 때문에 편해보이지만 코드를 한번 자세히 살펴보면 이상한 점이 존재한다.

함수 표현식을 더 권장하는 이유는 🌟 함수 표현식은 선언 전 함수 사용을 막을 수 있다 🌟

  • 함수 선언식을 사용하여 짠 코드

    [상황] 과거의 나는 열심히 sum 함수를 구현하였다. 하지만 새로온 신입이 나의 sum 함수를 발견하지 못하고 새롭게 sum 함수를 짜게 됐다. 이 상황에서 에러가 발생하는 것은 바로 나의 코드이다.

    // 과거의 내가 짠 코드
    console.log(sum(1, 3));
    function sum (a, b) {
        return a + b;
    }
    
    //... 500 번재 줄
    
    // 새로운 신입의 코드
    function sum (a, b) {
        return a + '+' + b + '=' + (a + b);
    }
    
    console.log(sum(2,3));
    
    //1+3=4
    //2+3=5
    

    그 이유는 함수 선언문 전체가 호이스팅 되어 나의 코드가 덮어 씌우기가 되버렸다. 😱 물론 극단적인 상황이지만 함수 표현식은 이러한 상황을 막아준다.

  • 함수 표현식을 사용하자

    console.log(sum(1, 2)); // error
    
    var sum = function (a, b) {
        return a + b;
    }
    
    //... 5000번째
    
    var sum = function (a, b) {
        return a + '+' + b + '=' + (a + b);
    }
    
    var c = sum(3, 4);
    console.log(c);
    

더 나아가 var 대신 const를 사용하면 호이스팅때문에 발생하는 에러를 막아준다는 글을 많이 보았을 것이다.

  • var의 변수 선언

    var name;
    
    name = 'nibble';
    
  • const의 변수선언

    const name; //error!!!!!
    

결론 🔥

const 변수 선언은 변수의 정의와 초기화가 한거번에 이루어지기 때문에 위같은 상황들을 막을 수 있다. const로 할당한 함수 표현식을 사용하자!


Reference

  • 코어자바스크립트