자바스크립트에서 함수를 생성하는 방법은 3가지가 있다. 이들 방식은 모두 같은 함수를 생성하지만, 각각의 방식에 따라 함수 동작이 미묘하게 차이가 난다.
- 함수 선언식 (function statement)
- 함수 표현식 (function expression)
- Function() 생성자 함수
각각의 방식에 대해 알아보기 전에 함수 선언문과 함수 표현식에서 함수를 생성하는 함수 리터럴의 개념을 먼저 살펴보자.
함수 리터럴 (익명 함수)
자바스크립트에서는 함수도 일반 객체처럼 값으로 취급된다. 때문에 객체 리터럴 방식으로 일반 객체를 생성할 수 있는 것처럼, 자바스크립트에서는 함수 리터럴을 이용해 함수를 생성할 수 있다.
함수 리터럴은 크게 네 부분으로 구성된다.
function add (x, y) {
return x + y;
}
① function 키워드 : 자바스크립트 함수 리터럴은 function 키워드로 시작한다.
② 함수명 : 함수명은 함수 몸체의 내부 코드에서 자신을 재귀적으로 호출하거나 또는 자바스크립트 디버거가 해당 함수를 구분하는 식별자로 사용된다. 함수명은 선택 사항이다. 함수명이 없는 함수를 익명 함수라 한다.
③ 매개변수 리스트 : 매개변수 타입을 기술하지 않는다.
④ 함수 몸체 : 실제 함수가 호출됐을 때 실행되는 코드 부분이다.
함수 생성하는 방법
1. 함수 선언식 (function statement)
함수 선언식은 앞에서 설명한 함수 리터럴 형태와 같다. 여기서 주의할 점은 함수 선언식으로 정의된 함수의 경우는 반드시 함수명이 정의되어 있어야 한다는 것이다.
// add() 함수 선언식
function add (x, y) {
return x + y;
}
console.log(add(3, 4)); // 7
2. 함수 표현식 (function expression)
자바스크립트에서는 함수도 하나의 값처럼 취급된다. 따라서 함수도 숫자나 문자열처럼 변수에 할당하는 것이 가능하다.
이런 방식으로 함수 리터럴로 하나의 함수를 만들고, 여기서 생성된 함수를 변수에 할당하여 함수를 생성하는 것을 함수 표현식이라고 한다.
함수 리터럴로 두 값을 더하는 함수를 생성한 다음, 이를 add 변수에 저장했다. 여기서 함수 리터럴로 생성한 함수는 함수명이 없으므로 익명 함수이다.
// add() 함수 표현식
var add = function (x, y) {
return x + y;
};
var plus = add;
console.log(add(3, 4)); // 7
console.log(plus(5, 6)); // 11
함수 표현식은 함수 선언문 문법과 거의 유사하다. 유일한 차이점은 함수 표현식 방법에서는 함수 이름이 선택 사항이며, 보통 사용하지 않는다는 것이다.
기명 함수 표현식
함수 이름이 포함된 함수 표현식을 기명 함수 표현식이라 한다.
var add = function sum(x, y) {
return x + y;
};
console.log(add(3, 4)); // 7
console.log(sum(3, 4)); // ReferenceError: sum is not defined 에러 발생
add() 함수 호출은 결과값이 성공적으로 리턴된 반면에, sum() 함수 호출은 에러가 발생했다. 이것은 함수 표현식에서 사용된 함수 이름이 외부 코드에서 접근 불가능하기 때문이다.
실제로 함수 표현식에 사용된 함수 이름은 정의된 함수 내부에서 해당 함수를 재귀적으로 호출하거나, 디버거 등에서 함수를 구분할 때 사용된다. 따라서 함수 이름으로 사용된 sum으로 함수 외부에서 해당 함수를 호출할 때 sum() 함수가 정의되어 있지 않다는 에러가 발생한다.
그렇다면 함수 선언식으로 정의한 add() 함수는 어떻게 함수 이름으로 함수 외부에서 호출이 가능할까?
함수 선언식으로 정의된 add() 함수는 자바스크립트 엔진에 의해 다음과 같은 함수 표현식 형태로 변경되기 때문이다.
// add() 함수 선언식
var add = function add (x, y) {
return x + y;
}
함수 이름과 함수 변수의 이름이 add로 같으므로, 함수 이름으로 함수가 호출되는 것처럼 보이지만, 실제로는 add 함수 변수로 함수 외부에서 호출이 가능하게 된 것이다.
앞서 설명했듯이 함수 표현식에서는 함수 이름이 선택 사항이지만, 이러한 함수 이름을 이용하면 함수 코드 내부에서 함수 이름으로 함수의 재귀적인 호출 처리가 가능하다.
// 팩토리얼 함수 > 함수 표현식
var factorialVar = function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
};
console.log(factorialVar(3)); // 6
console.log(factorial(3)); // ReferenceError: factorial is not defined
9라인을 보면, 함수 외부에서는 함수 변수 factorialVar로 함수를 호출했으며, 함수 내부에서 이뤄지는 재귀 호출은 factorial() 함수 이름으로 처리한다는 것을 알 수 있다. 앞서 설명한 것과 마찬가지로 함수명 factorial()으로 함수 외부에서 해당 함수를 호출하지 못해 에러가 발생한다.
화살표 함수 (Arrow Function)
화살표 함수(=>) 를 이용하면 함수 표현식을 더 간단한 방법으로 함수를 정의할 수 있다.
// add() 함수 선언식
var add = function add (x, y) {
return x + y;
}
// 화살표 함수
var add = (x, y) => x + y;
3. Function() 생성자 함수 (자주 사용되지 않음)
자바스크립트의 함수도 Function() 이라는 기본 내장 생성자 함수로부터 생성된 객체라고 볼 수 있다.
Function() 생성자 함수로 함수를 생성하는 문법은 다음과 같다.
new Function (arg1, arg2, ... argN, functionBody)
▪ arg1, arg2, ..., argN - 함수의 매개변수
▪ functionBody - 함수가 호출될 때 실행될 코드를 포함한 문자열
var add = new Function("x", "y", "return x + y");
console.log(add(3, 4)); // 7
차이점
함수 호이스팅 (Function Hoisting)
- 함수 선언식은 호이스팅에 영향을 받는다.
- 함수 표현식은 호이스팅에 영향을 받지 않는다.
add(2,3); // 5
// add() 함수 선언식
function add (x, y) {
return x + y;
}
add(3, 4); // 7
1라인을 보면, 이 시점에서는 아직 add() 함수가 정의되지 않았음에도 4라인에서 정의된 add() 함수를 호출하는 것이 가능하다. 이것은 함수가 자신이 위치한 코드에 상관없이 함수 선언식으로 정의한 함수의 유효 범위는 코드의 맨 처음부터 시작한다는 것을 확인할 수 있다. 이것을 함수 호이스팅이라고 부른다.
add(2,3); // uncaught type error
// add() 함수 표현식
var add = function (x, y) {
return x + y;
}
add(3, 4); // 7
add() 함수는 4라인에서 함수 표현식 형태로 정의되어 있어 호이스팅이 일어나지 않는다. 따라서 8라인과 같이 함수가 생성된 이후에 호출이 가능하다.
이러한 함수 호이스팅이 발생하는 원인은 자바스크립트의 변수 생성(Instantiation)과 초기화(Initialization)의 작업이 분리돼서 진행되기 때문이다.
세미콜론(;)의 사용
- 함수 선언식으로 함수를 선언할 때 세미콜론(;)을 붙이지 않는다.
- 함수 표현식으로 함수를 선언할 때 세미콜론(;)을 붙인다.