함수 선언식 (Function Declaration)
아래와 같이 작성하여 함수를 만들 수 있다.
// 함수를 만드는 방법
function 함수이름() {
필요한 동작;
}
함수를 호출할 때 어떤 값을 전달해서
함수가 동작할 때 그 값을 사용하게 하고 싶다면
소괄호 안에 파라미터를 작성한다.
// 함수를 만드는 방법
function 함수이름(파라미터) {
필요한 동작;
}
함수가 호출된 자리에 되돌려줄 값
즉 리턴 값이 필요하다면
동작 부분 안에 return 키워드를 통해 리턴 값을 작성한다.
// 함수를 만드는 방법
function 함수이름(파라미터) {
필요한 동작;
return 리턴값;
}
위와 같이 function 키워드로 시작해서 함수를 만드는 방식을
함수 선언 (Function Declaration)이라고 한다.
이 방식은 필자 TIL을 처음부터 봤었다면 아마 알고 있던 내용일 것이다.
복습하기 - link
함수를 만드는 방법은 함수 선언 말고 한 가지 더 존재한다.
함수 표현식 (Function Expression) - 함수 선언을 변수에 할당하는 방식
const printStudy = function() {
console.log('Modern JS');
}
printStudy();
위 코드를 보면 'printStudy' 라는 변수에 함수 선언을 값처럼 할당하고 있다.
실행하면 정상적으로 작동하는 것을 확인할 수 있다.
이렇게 함수 선언을 값처럼 활용하는 방식을 가리켜서
함수 표현식 (Function Expression)이라고 부른다.
한 가지 주의해야할 부분은
함수 선언을 변수에 할당하는 모습만 함수 표현식으로 보는 것이 아니다.
아래 코드를 살펴보면..
이벤트를 만들 때 addEventListener의 두 번째 파라미터로 함수를 선언해서 전달한 것을
결과적으로는 함수 선언을 값처럼 활용한다는 점에서 함수 표현식이라고 할 수 있다.
정리하면 함수 표현식은 변수에 할당하는 것이 포인트가 아니라
함수 선언을 값처럼 사용하는 방식이다.
그렇다면 함수 선언과 함수 표현식은 어떤 차이가 있을까?
그동안 사용하던 함수 선언 방식은 사실, 아래와 같이 선언하기 전에 호출할 수가 있었다.
printStudy();
function printStudy() {
console.log("Modern JS");
}
분명히 함수 선언 보다 먼저 함수를 호출했는데도 문제 없이 잘 작동되는 것을 확인할 수 있는데
이렇게 선언문이 위쪽으로 끌어 올려지는 현상을 호이스팅(Hoisting)이라고 한다.
반면에 아래와 같이 일반적으로 변수에 할당해서 함수를 만드는 함수 표현식은
변수의 특성상 선언 이전에 접근을 할 수 없다.
printStudy();
const printStudy = function () {
console.log("Modern JS");
};
또 한가지는 스코프의 차이가 있다.
함수 선언은 변수의 var 키워드처럼 함수 스코프를 가진다.
아래와 같이 함수 안에 선언된 함수는 함수 밖에서 호출할 수 없지만
아래와 같이 함수가 아닌 다른 코드 블록에서 따로 따로
함수 선언을 하게 되면 모두 전역적으로 호출이 가능하다. (어떻게보면.. 당연한 이야기)
const x = 4;
if(x<5){
function PrintJS() {
console.log("JavaScript");
}
}
{
function printStudy() {
console.log("Modern JS");
}
}
printStudy();
PrintJS();
반면에 함수 표현식의 경우에는 할당된 변수에 따라 스코프가 결정된다.
var PrintJS = function () {
console.log("JavaScript");
};
let printHi = function () {
console.log("Hi");
};
const printStudy = function () {
console.log("Modern JS");
};
그렇다면 함수를 만들 때 둘 중 어떤 방식을 활용하는 것이 좋을까?
function PrintJS () {
console.log("JavaScript");
};
코드의 가독성 면에서는 변수 선언과 함수 선언 부분을 명확하게 구분할 수 있고
함수를 호출할 때도 자유로운 위치에서 함수를 호출할 수 있다는 점을 장점으로
함수 선언 방식을 쓰는게 좋을 수도 있고
const printStudy = function () {
console.log("Modern JS");
};
반드시 선언 이후에 호출할 수 있다는 점이
가독성 면에서는 오히려 코드의 흐름을 더 쉽게 파악할 수 있고
변수의 스코프도 활용할 수 있다는 부분을 장점으로
함수 표현식을 쓰는게 좋을 수도 있다.
둘 중 어느 방식을 선택하더라도 크게 문제되지는 않지만
두 방식이 서로 다른 특성을 가지고 있는 만큼 가급적 둘 중 하나를 선택해서
최대한 일관된 방식으로 함수를 만드는 것이 좋다.
일반적으로 어떤 개념을 설명할 때는 함수 표현식 보다는
선언 부분이라는 의미가 좀 더 명확하게 구분되는 함수 선언이 보편적으로 사용되기 때문에
필자는 함수 선언을 위주로 공부할 계획이다.
기명 함수 표현식 (Named Function Expression)
함수 표현식으로 함수를 만들 때 선언하는 함수에 이름을 붙여줄 수도 있다.
이름이 있는 함수 표현식, 즉 기명 함수 표현식이라고 부르는데
함수 표현식으로 함수가 할당된 변수에는 자동으로 name이라는 프로퍼티를 가진다.
const sayHi = function () {
console.log('Hi');
};
console.log(sayHi.name); // sayHi
위와 같이 이름이 없는 함수를 변수에 할당할 때는
변수의 name 프로퍼티는 변수 이름 그 자체를 문자열로 가지게 된다.
하지만 함수에 이름을 붙여주게 되면..
const sayHi = function printHiInConsole() {
console.log('Hi');
};
console.log(sayHi.name); // printHiInConsole
위와 같이 name 속성은 함수 이름을 문자열로 갖게 된다.
이 함수 이름은 함수 내부에서 함수 자체를 가리킬 때 사용할 수 있지만
아래와 같이 함수를 외부에서 함수를 호출할 때 사용할 수는 없다.
const sayHi = function printHiInConsole() {
console.log('Hi');
};
printHiInConsole(); // ReferenceError
기명 함수 표현식은 일반적으로 함수 내부에서 함수 자체를 가리킬 때 사용한다.
그 예시를 보자면..
함수를 호출할 때 이 때 사용하게 되는 일련의 값들을 argument 라고하는데
아래 코드를 보면 아규먼트로 숫자 값을 전달하고 전달받은 그 값이 0이 될 때까지 하나씩 값을 줄이면서
자기 자신을 호출하는 'countdown'이라는 함수를 함수 표현식으로 작성하였다.
let countdown = function(n) {
console.log(n);
if (n === 0) {
console.log('End!');
} else {
countdown(n - 1);
}
};
countdown(5);
이런식으로 자기 자신을 부르는 함수를 재귀 함수(Recursive function) 라고 부른다.
그런데 아래와 같이 이 함수를 복사하려고 다른 변수에 똑같이 담아봤는데
countdown 변수에 담긴 값이 변하게 되면 문제가 발생한다.
마지막 줄에서 myFunction함수를 호출 했을 때 5가 실행되긴 했지만
'countdown(n-1)' 동작을 수행할 때 호출하려는 countdown 함수가 이미 null 값으로 변경되었기 때문에
함수가 아니라는 타입에러가 발생한다.
이런 상황을 방지하기 위해 함수 내부에서 함수 자신을 사용하려고 하면
함수 표현식에서는 반드시 기명 함수 표현식을 사용하는 것이 좋다.
함수 표현식을 작성할 때
함수에 이름을 지정할 수 있다는 점과
위와 같이 함수 내에서 함수를 가리켜야할 때는 꼭 함수 이름을 작성해주어야 한다.
'JS' 카테고리의 다른 글
(9) Modern JavaScript - 값으로서 함수 (0) | 2024.11.26 |
---|---|
(8) Modern JavaScript - 즉시 실행 함수 (IIFE) (0) | 2024.11.25 |
(6) Modern JavaScript - 변수와 스코프 (0) | 2024.11.21 |
(5) Modern JavaScript - null 병합 연산자 (0) | 2024.11.21 |
(4) Modern JavaScript - AND , OR 연산 방식과 우선 순위 (0) | 2024.11.21 |