function factorial(n) {
if (n < 1) return 1;
return n * factorial(n-1);
}
factorial(3); // 6
다음과 같은 factorial 함수가 있다.
우리는 이와 같은 문을 함수 선언문(function Declaration)이라고 하고, 이때의 factorial은 함수 이름이 된다. 그리고 이렇게 선언된 함수는 해당 함수이름으로 등록된 식별자를 호출함으로서 실행할수 있다.
하지만 다음과 같은 상황을 보자.
(function factorial(n){
if (n < 1) return 1;
return n * factorial(n-1);
})
factorial(3); // ReferenceError: factorial is not defined
이와 같이 함수 선언문이 피연산자에 위치해있을때, 해당 함수의 이름을 통해 호출이 불가능하다. 왜 이런일이 일어날까?
앞서 표현식에서 살펴보았듯, 함수 선언문은 피연산자에 위치해 있을때, 자바스크립트 엔진에 의해 함수 리터럴 표현식으로서 해석된다. 그리고 이때 작성된 함수 선언문의 함수 이름은, 함수 스코프 내에 등록된다. 즉, 함수 바깥에서 해당 식별자를 참고할수는 없다. 그렇기 때문에 위의 factorial 함수의 'factorial' 식별자는, 함수 내부에서만 참조가 가능한 식별자이고, 스코프 외부에서는 factorial 식별자를 참조 할 수 없게 된다.
이를 해결하기 위해 반환되는 값 (함수 객체)을 변수에 담는다.
var f1 = function factorial(n) {
if (n < 1) return 1;
return n * factorial(n-1);
}
f1(3);
이렇게 함으로서, 함수를 담고있는 식별자(f1)이 전역 스코프에 등록되고, 해당 함수를 참조하며 호출할수 있게된다. 이때, 함수 내부적으로는 'factorial' 식별자로 함수를 호출할수 있다는 점을 기억하자.
그렇다면 최상단의 코드는 어떻게 호출이 가능했던걸까?
function factorial(n) {
if (n < 1) return 1;
return n * factorial(n-1);
}
factorial(3); // 6
그 이유는 함수 선언문(피연산자에 위치하지 않은)은 자바스크립트 엔진에서 암묵적으로, 해당 함수이름을 현재 실행컨텍스트의 환경레코드에 식별자로서 등록시켜주기 때문이다.
다르게 말하면, 함수 선언문을 함수 표현식으로 변환하여, 해당 표현식이 반환하는 함수 객체 값을, '함수이름' 식별자에 등록시켜주는것이다.
단, 그렇다고 함수 선언문과 함수 표현식이 동일하게 봐서는 안된다. 둘은 명백하게, 선언되는 시점이 다르다.
이미 JavaScript를 공부한 사람이라면 아는 사실이겠지만, 함수 선언문은 호이스팅되는 특징이 있다. 즉, 함수 선언문이 함수 표현식으로 변환되고, 해당 표현식이 평가되어 생성되는 함수 객체를 '함수이름' 식별자에 담는 과정이, 코드 평가 단계(실행이 아니다)에서 이루어진다.