function 함수이름() {
동작
}// 기본 함수 만들기
function 함수이름() {
동작
return 리턴값
}
//함수를 호출할 때 어떤 값을 전달해서 함수가 동작할 때 그 값을 사용하게 하고 싶다면 소괄호 안에 파라미터를 작성할 수도 있다. 또한 함수가 호출된 자리에 되돌려 줄 값이 필요하다면 리턴값을 작성해 줄 수도 있다.
이렇게 function 키워드를 통해서 함수를 만드는 방식을 함수 선언 (Function Declaraion) 이라고 한다. 함수 선언은 함수 스코프를 가지기 때문에 코드 블록 안에서 함수를 선언하더라도 코드 블록 밖에서 아무런 문제 없이 사용할 수 있다.
const printCodeit = function () {
console.log('Codeit');
}
printCodeit();
함수 선언을 변수에 할당하여 값처럼 활용하는 방식도 있다. 그런데 함수표현식은 변수에 할당하는 것이 포인트가 아니라 함수 선언을 값처럼 사용하는 방식이라는 점이다.
우리가 그동안 사용하던 함수 선언 방식은 사실 선언을 하기 전에 호출할 수 있었다. 반면 일반적으로 변수에 할당해서 함수를 만드는 함수 표현식은 변수의 특성상 선언 이전에 접근을 할 수가 없다.
두 번째로 스코프의 차이가 있다. 함수 선언은 변수의 var 키워드처럼 함수 스코프를 가진다. 함수 안에 선언된 함수는 함수 밖에서 호출할 수 없지만, 함수가 아닌 다른 코드 블록에서 함수 선언을 하게 되면 모두 전역적으로 호출이 가능해진다. 반면 함수 표현식의 경우 할당된 변수에 따라서 스코프가 결정된다.
코드의 가독성 면에서 변수 선언과 함수 선언 부분을 명확하게 구분할 수 있고, 함수를 호출할 때도 자유로운 위치에서 함수를 호출할 수 있다는 점을 장점으로 함수 선언 방식을 권장하는 의견과 반드시 선언 이후에 호출할 수 있다는 점이 가독성 면에서는 오히려 코드의 흐름을 더 쉽게 파악할 수 있고 변수의 스코프도 활용할 수 있다는 장점으로 함수 표현식을 권장하는 사람도 있다. 최대한 일관적으로 사용하는 것이 좋다.
일반적으로 어떤 개념을 설명할 때는 함수 표현식보다는 선언 부분이라는 의미가 좀 더 명확하게 구분되는 함수 선언이 보편적으로 사용된다.
함수 표현식으로 함수를 만들 때는 선언하는 함수에 이름을 붙여줄 수도 있다. 이를 기명 함수 표현식이라고 하는데, 함수 표현식으로 함수가 할당된 변수에는 자동으로 name이라는 프로퍼티를 가진다.
const sayHi = function () {
console.log('Hi');
};
console.log(sayHi.name); // sayHi
이렇게 이름이 없는 함수를 변수에 할당할 때는 변수의 name 프로퍼티는 변수 이름 그 자체를 문자열로 가지게 된다. 하지만 함수에 이름을 붙여주게 되면, name 속성은 함수 이름을 문자열로 갖게 된다.
const sayHi = function printHiInConsole() {
console.log('Hi');
};
console.log(sayHi.name); // printHiInConsole
이 함수 이름은 함수 내부에서 함수 자체를 가리킬 때 사용할 수 있고 함수를 외부에서 함수를 호출할 때 사용할 수는 없다.
const sayHi = function printHiInConsole() {
console.log('Hi');
};
printHiInConsole(); // ReferenceError
기명 함수 표현식은 일반적으로 함수 내부에서 함수 자체를 가리킬 때 사용된다.
let countdown = function(n) {
console.log('End!');
} else {
countdown(n - 1);
}
};
countdown(5);
위와 같이 아규먼트로 숫자 값을 전달하고 전달받은 그 값이 0이 될 때까지 하나씩 값을 줄이면서 자기 자신을 호출하는 countdown이라는 함수를 함수 표현식으로 작성한 것인데, 이런 식으로 자기 자신을 부르는 함수를 재귀 함수 (Recursive function)라고 부른다.
그런데 만약 이 함수를 복사하려고 다른 변수에 똑같이 담았다가, countdown 변수에 담긴 값이 변하게 되면 문제가 발생한다.
let countdown = function(n) {
console.log(n);
if (n === 0) {
console.log('End!');
} else {
countdown(n - 1);
}
};
let myFunction = countdown;
countdown = null;
myFunction(5); // TypeError
마지막 줄에서 myFunction 함수를 호출했을 때, 함수가 실행되긴 하지만, 6번줄 동작을 수행할 때 호출하려는 countdown 함수가 이미 12번에서 null 값으로 변경되었기 때문에 함수가 아니라는 TypeError가 발생한 것이다. 이런 상황을 방지하기 위해서 함수 내부에서 함수 자신을 사용하려고 하면 함수표현식에서는 반드시 기명 함수 표현식을 사용하는 것이 좋다.
let countdown = function printCountdown(n) {
console.log(n);
if (n === 0) {
console.log('End!');
} else {
printCountdown(n - 1);
}
};
let myFunction = countdown;
countdown = null;
myFunction(5); // 정상적으로 동작
함수 표현식을 작성할 때, 함수에 이름을 지정할 수 있다는 점과 특히 이렇게 함수 내에서 함수를 가리켜야 할 때는 꼭 함수 이름을 작성해주는 것이 안전하다.