# 함수 표현식

자바스크립트는 함수를 특별한 종류의 값으로 취급합니다. 다른 언어에서처럼 "특별한 동작을 하는 구조"로 취급되지 않습니다.

이전 챕터에서 *함수 선언(Function Declaration), 함수 선언문* 방식으로 함수를 만들었습니다. 아래와 같이 말이죠.

```javascript
function sayHi() {
  alert( "Hello" );
}
```

함수 선언 방식 외에 *함수 표현식(Function Expression)* 을 사용해서 함수를 만들 수 있습니다.

함수 표현식으로 함수를 생성해보겠습니다.

```javascript
let sayHi = function() {
  alert( "Hello" );
};
```

함수를 생성하고 변수에 값을 할당하는 것처럼 함수가 변수에 할당되었습니다. 함수가 어떤 방식으로 만들어졌는지에 관계없이 함수는 값이고, 따라서 변수에 할당할 수 있습니다. 위 예시에선 함수가 변수 `sayHi`에 저장된 값이 되었습니다.

위 예시를 간단한 말로 풀면 다음과 같습니다: “함수를 만들고 그 함수를 변수 `sayHi`에 할당하기”

함수는 값이기 때문에 `alert`를 이용하여 함수 코드를 출력할 수도 있습니다.

```javascript
function sayHi() {
  alert( "Hello" );
}

alert( sayHi ); // 함수 코드가 보임
```

마지막 줄에서 `sayHi`옆에 괄호가 없기 때문에 함수는 실행되지 않습니다. 어떤 언어에선 괄호 없이 함수 이름만 언급해도 함수가 실행됩니다. 하지만 자바스크립트는 괄호가 있어야만 함수가 호출됩니다.

자바스크립트에서 함수는 값입니다. 따라서 함수를 값처럼 취급할 수 있습니다. 위 코드에선 함수 소스 코드가 문자형으로 바뀌어 출력되었습니다.

함수는 `sayHi()`처럼 호출할 수 있다는 점 때문에 일반적인 값과는 조금 다릅니다. 특별한 종류의 값이죠.

하지만 그 본질은 값이기 때문에 값에 할 수 있는 일을 함수에도 할 수 있습니다.

변수를 복사해 다른 변수에 할당하는 것처럼 함수를 복사해 다른 변수에 할당할 수도 있습니다.

```javascript
function sayHi() {   // (1) 함수 생성
  alert( "Hello" );
}

let func = sayHi;    // (2) 함수 복사

func(); // Hello     // (3) 복사한 함수를 실행(정상적으로 실행됩니다)!
sayHi(); // Hello    //     본래 함수도 정상적으로 실행됩니다.
```

위 예시에서 어떤 일이 일어났는지 자세히 알아보겠습니다.

1. `(1)`에서 함수 선언 방식을 이용해 함수를 생성합니다. 생성한 함수는 `sayHi`라는 변수에 저장됩니다.
2. `(2)` 에선 `sayHi`를 새로운 변수 `func`에 복사합니다. 이때 `sayHi` 다음에 괄호가 없다는 점에 유의하시기 바랍니다. 괄호가 있었다면 `func = sayHi()` 가 되어 `sayHi` *함수* 그 자체가 아니라, *함수 호출 결과(함수의 반환 값)* 가 `func`에 저장되었을 겁니다.
3. 이젠 `sayHi()` 와 `func()`로 함수를 호출할 수 있게 되었습니다.

함수 `sayHi`는 아래와 같이 함수 표현식을 사용해 정의할 수 있습니다.

```javascript
let sayHi = function() {
  alert( "Hello" );
};

let func = sayHi;
// ...
```

동작 결과는 동일합니다.

끝에 세미 콜론은 왜 있나요?

함수 표현식의 끝에 왜 세미 콜론 `;`이 붙는지 의문이 들 수 있습니다. 함수 선언문에는 세미 콜론이 없는데 말이죠.

```javascript
function sayHi() {
  // ...
}

let sayHi = function() {
  // ...
};
```

이유는 간단합니다.

* `if { ... }`, `for { }`, `function f { }` 같이 중괄호로 만든 코드 블록 끝엔 `;`이 없어도 됩니다.
* 함수 표현식은 `let sayHi = ...;`과 같은 구문 안에서 값의 역할을 합니다. 코드 블록이 아니고 값처럼 취급되어 변수에 할당되죠. 모든 구문의 끝엔 세미 콜론 `;`을 붙이는 게 좋습니다. 함수 표현식에 쓰인 세미 콜론은 함수 표현식 때문에 붙여진 게 아니라, 구문의 끝이기 때문에 붙여졌습니다.

### [콜백 함수](https://ko.javascript.info/function-expressions#ref-444)

함수를 값처럼 전달하는 예시, 함수 표현식에 관한 예시를 좀 더 살펴보겠습니다.

매개변수가 3개 있는 함수, `ask(question, yes, no)`를 작성해보겠습니다. 각 매개변수에 대한 설명은 아래와 같습니다.

`question`질문`yes`"Yes"라고 답한 경우 실행되는 함수`no`"No"라고 답한 경우 실행되는 함수

함수는 반드시 `question(질문)`을 해야 하고, 사용자의 답변에 따라 `yes()` 나 `no()`를 호출합니다.

```javascript
function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

function showOk() {
  alert( "동의하셨습니다." );
}

function showCancel() {
  alert( "취소 버튼을 누르셨습니다." );
}

// 사용법: 함수 showOk와 showCancel가 ask 함수의 인수로 전달됨
ask("동의하십니까?", showOk, showCancel);
```

이렇게 함수를 작성하는 방법은 실무에서 아주 유용하게 쓰입니다. 면대면으로 질문하는 것보다 위처럼 컨펌창을 띄워 질문을 던지고 답변을 받으면 간단하게 설문조사를 진행할 수 있습니다. 실제 상용 서비스에선 컨펌 창을 좀 더 멋지게 꾸미는 등의 작업이 동반되긴 하지만, 일단 여기선 그게 중요한 포인트는 아닙니다.

**함수 `ask`의 인수, `showOk`와 `showCancel`은&#x20;*****콜백 함수*****&#x20;또는&#x20;*****콜백*****이라고 불립니다.**

함수를 함수의 인수로 전달하고, 필요하다면 인수로 전달한 그 함수를 "나중에 호출(called back)"하는 것이 콜백 함수의 개념입니다. 위 예시에선 사용자가 "yes"라고 대답한 경우 `showOk`가 콜백이 되고, "no"라고 대답한 경우 `showCancel`가 콜백이 됩니다.

아래와 같이 함수 표현식을 사용하면 코드 길이가 짧아집니다.

```javascript
function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

ask(
  "동의하십니까?",
  function() { alert("동의하셨습니다."); },
  function() { alert("취소 버튼을 누르셨습니다."); }
);
```

`ask(...)` 안에 함수가 선언된 게 보이시나요? 이렇게 이름 없이 선언한 함수는 *익명 함수(anonymous function)* 라고 부릅니다. 익명 함수는 (변수에 할당된 게 아니기 때문에) `ask` 바깥에선 접근할 수 없습니다. 위 예시는 의도를 가지고 이렇게 구현하였기 때문에 바깥에서 접근할 수 없어도 문제가 없습니다.

자바스크립트를 사용하다 보면 콜백을 활용한 코드를 아주 자연스레 만나게 됩니다. 이런 코드는 자바스크립트의 정신을 대변합니다.

함수는 "동작"을 나타내는 값입니다.

문자열이나 숫자 등의 일반적인 값들은 *데이터*를 나타냅니다.

함수는 하나의 \*동작(action)\*을 나타냅니다.

동작을 대변하는 값인 함수를 변수 간 전달하고, 동작이 필요할 때 이 값을 실행할 수 있습니다.

### [함수 표현식 vs 함수 선언문](https://ko.javascript.info/function-expressions#ref-445)

함수 표현식과 선언문의 차이에 대해 알아봅시다.

첫 번째는 문법입니다. 코드를 통해 어떤 차이가 있는지 살펴봅시다.

* *함수 선언문:* 함수는 주요 코드 흐름 중간에 독자적인 구문 형태로 존재합니다.

  ```javascript
  // 함수 선언문
  function sum(a, b) {
    return a + b;
  }
  ```
* *함수 표현식:* 함수는 표현식이나 구문 구성(syntax construct) 내부에 생성됩니다. 아래 예시에선 함수가 할당 연산자 `=`를 이용해 만든 “할당 표현식” 우측에 생성되었습니다.

  ```javascript
  // 함수 표현식
  let sum = function(a, b) {
    return a + b;
  };
  ```

두 번째 차이는 자바스크립트 엔진이 *언제* 함수를 생성하는지에 있습니다.

**함수 표현식은 실제 실행 흐름이 해당 함수에 도달했을 때 함수를 생성합니다. 따라서 실행 흐름이 함수에 도달했을 때부터 해당 함수를 사용할 수 있습니다.**

위 예시를 이용해 설명해 보도록 하겠습니다. 스크립트가 실행되고, 실행 흐름이 `let sum = function…`의 우측(함수 표현식)에 도달 했을때 함수가 생성됩니다. 이때 이후부터 해당 함수를 사용(할당, 호출 등)할 수 있습니다.

하지만 함수 선언문은 조금 다릅니다.

**함수 선언문은 함수 선언문이 정의되기 전에도 호출할 수 있습니다.**

따라서 전역 함수 선언문은 스크립트 어디에 있느냐에 상관없이 어디에서든 사용할 수 있습니다.

이게 가능한 이유는 자바스크립트의 내부 알고리즘 때문입니다. 자바스크립트는 스크립트를 실행하기 전, 준비단계에서 전역에 선언된 함수 선언문을 찾고, 해당 함수를 생성합니다. 스크립트가 진짜 실행되기 전 "초기화 단계"에서 함수 선언 방식으로 정의한 함수가 생성되는 것이죠.

스크립트는 함수 선언문이 모두 처리된 이후에서야 실행됩니다. 따라서 스크립트 어디서든 함수 선언문으로 선언한 함수에 접근할 수 있는 것입니다.

예시를 살펴봅시다.

```javascript
sayHi("John"); // Hello, John

function sayHi(name) {
  alert( `Hello, ${name}` );
}
```

함수 선언문, `sayHi`는 스크립트 실행 준비 단계에서 생성되기 때문에, 스크립트 내 어디에서든 접근할 수 있습니다.

그러나 함수 표현식으로 정의한 함수는 함수가 선언되기 전에 접근하는 게 불가능합니다.

```javascript
sayHi("John"); // error!

let sayHi = function(name) {  // (*) 마술은 일어나지 않습니다.
  alert( `Hello, ${name}` );
};
```

함수 표현식은 실행 흐름이 표현식에 다다랐을 때 만들어집니다. 위 예시에선 `(*)`로 표시한 줄에 실행 흐름이 도달했을 때 함수가 만들어집니다. 아주 늦죠.

세 번째 차이점은, 스코프입니다.

**엄격 모드에서 함수 선언문이 코드 블록 내에 위치하면 해당 함수는 블록 내 어디서든 접근할 수 있습니다. 하지만 블록 밖에서는 함수에 접근하지 못합니다.**

예시를 들어 설명해 보겠습니다. 런타임에 그 값을 알 수 있는 변수 `age`가 있고, 이 변수의 값에 따라 함수 `welcome()`을 다르게 정의해야 하는 상황입니다. 그리고 함수 `welcome()`은 나중에 사용해야 하는 상황이라고 가정해 보죠.

함수 선언문을 사용하면 의도한 대로 코드가 동작하지 않습니다.

```javascript
let age = prompt("나이를 알려주세요.", 18);

// 조건에 따라 함수를 선언함
if (age < 18) {

  function welcome() {
    alert("안녕!");
  }

} else {

  function welcome() {
    alert("안녕하세요!");
  }

}

// 함수를 나중에 호출합니다.
welcome(); // Error: welcome is not defined
```

함수 선언문은 함수가 선언된 코드 블록 안에서만 유효하기 때문에 이런 에러가 발생합니다.

또 다른 예시를 살펴봅시다.

```javascript
let age = 16; // 16을 저장했다 가정합시다.

if (age < 18) {
  welcome();               // \   (실행)
                           //  |
  function welcome() {     //  |
    alert("안녕!");        //  |  함수 선언문은 함수가 선언된 블록 내
  }                        //  |  어디에서든 유효합니다
                           //  |
  welcome();               // /   (실행)

} else {

  function welcome() {
    alert("안녕하세요!");
  }
}

// 여기는 중괄호 밖이기 때문에
// 중괄호 안에서 선언한 함수 선언문은 호출할 수 없습니다.

welcome(); // Error: welcome is not defined
```

그럼 `if`문 밖에서 `welcome` 함수를 호출할 방법은 없는 걸까요?

함수 표현식을 사용하면 가능합니다. `if`문 밖에 선언한 변수 `welcome`에 함수 표현식으로 만든 함수를 할당하면 되죠.

이제 코드가 의도한 대로 동작합니다.

```javascript
let age = prompt("나이를 알려주세요.", 18);

let welcome;

if (age < 18) {

  welcome = function() {
    alert("안녕!");
  };

} else {

  welcome = function() {
    alert("안녕하세요!");
  };

}

welcome(); // 제대로 동작합니다.
```

물음표 연산자 `?`를 사용하면 위 코드를 좀 더 단순화할 수 있습니다.

```javascript
let age = prompt("나이를 알려주세요.", 18);

let welcome = (age < 18) ?
  function() { alert("안녕!"); } :
  function() { alert("안녕하세요!"); };

welcome(); // 제대로 동작합니다.
```

함수 선언문과 함수 표현식 중 무엇을 선택해야 하나요?

제 경험에 따르면 함수 선언문을 이용해 함수를 선언하는 걸 먼저 고려하는 게 좋습니다. 함수 선언문으로 함수를 정의하면, 함수가 선언되기 전에 호출할 수 있어서 코드 구성을 좀 더 자유롭게 할 수 있습니다.

함수 선언문을 사용하면 가독성도 좋아집니다. 코드에서 `let f = function(…) {…}`보다 `function f(…) {…}` 을 찾는 게 더 쉽죠. 함수 선언 방식이 더 “눈길을 사로잡습니다”.

그러나 어떤 이유로 함수 선언 방식이 적합하지 않거나, (위 예제와 같이) 조건에 따라 함수를 선언해야 한다면 함수 표현식을 사용해야 합니다.

### [요약](https://ko.javascript.info/function-expressions#ref-446)

* 함수는 값입니다. 따라서 함수도 값처럼 할당, 복사, 선언할 수 있습니다.
* “함수 선언(문)” 방식으로 함수를 생성하면, 함수가 독립된 구문 형태로 존재하게 됩니다.
* “함수 표현식” 방식으로 함수를 생성하면, 함수가 표현식의 일부로 존재하게 됩니다.
* 함수 선언문은 코드 블록이 실행되기도 전에 처리됩니다. 따라서 블록 내 어디서든 활용 가능합니다.
* 함수 표현식은 실행 흐름이 표현식에 다다랐을 때 만들어집니다.

함수를 선언해야 한다면 함수가 선언되기 이전에도 함수를 활용할 수 있기 때문에, 함수 선언문 방식을 따르는 게 좋습니다. 함수 선언 방식은 코드를 유연하게 구성할 수 있도록 해주고, 가독성도 좋습니다.

함수 표현식은 함수 선언문을 사용하는게 부적절할 때에 사용하는 것이 좋습니다. 이번 챕터에서 함수 선언문을 사용해야만 하는 경우를 몇 가지 알아보았는데, 튜토리얼 뒤쪽에서 좀 더 깊게 해당 사례를 살펴보도록 하겠습니다.

\
출처 : [Modern Javascript 튜토리얼](https://ko.javascript.info/function-expressions)
