let sayHi = function() {
  alert("Hello");
};
함수를 생성하고 변수에 값을 할당하는 것처럼 함수가 변수에 할당되었습니다.
함수가 어떤 방식으로 만들어졌는지에 관계없이 함수는 값이고, 따라서 변수에 할당할 수 있습니다.
함수는 값이기 때문에 아래와 같이 alert를 이용하여 함수 코드를 출력할 수도 있습니다.
function sayHi() {
  alert( "Hello" );
}
alert( sayHi ); // hello가 아닌 함수 코드가 보입니다.
마지막 줄에서 sayHi 옆에 괄호가 없기 때문에 함수는 실행되지 않습니다.
변수를 복사해 다른 변수에 할당하는 것처럼 함수도 다른 변수에 할당할 수도 있습니다.
function sayHi() { // (1)
  alert( "Hello" );
}
let func = sayHi; // (2)
func(); // Hello      (3)
sayHi(); // Hello
if {...}, for {}, function f { } 같이 중괄호로 만든 코드 블록 끝엔 ;이 없어도 됩니다.
함수 표현식은 let sayHi = ...;와 같은 구문 안에서 값의 역할을 합니다.
코드 블록이 아니고 값처럼 취급되어 변수에 할당되죠.
그래서 모든 구문의 끝엔 세미 콜론을 붙이는 게 좋습니다.
함수 표현식에 쓰인 세미 콜론은 함수 표현식 때문에 붙여진 게 아니라, 구문의 끝이기 떄문에 붙여졌습니다.
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은 콜백 함수 또는 콜백이라고 불립니다.
함수를 함수의 인수로 전달하고, 필요하다면 인수로 전달한 그 함수를 "나중에 호출(called back)"하는 것이 콜백 함수의 개념입니다.
위의 예시에서는 사용자가 "yes"라고 대답한 경우 showOk가 콜백이 되고, "no"라고 대답한 경우 showCancel가 콜백이 됩니다.
아래와 같이 함수 표현식을 사용하면 코드 길이가 짧아집니다.
function ask(question, yes, no) {
 if (confirm(question)) yes()
 else no();
}
ask(
 "동의하십니까?",
 function() { alert("동의하셨습니다."); },
 function() { alert("취소 버튼을 누르셨습니다."); }
);
수 표현식과 선언문의 차이에 대해 알아봅시다.
첫 번째는 문법입니다. 코드를 통해 어떤 차이가 있는지 살펴봅시다.
// 함수 선언문
function sum(a, b) {
  return a + b;
}
=를 이용해 만든 "할당 표현식" 우측에 생성되었습니다.    // 함수 표현식
    let sum = function(a, b) {
      return a + b;
    };
두 번째 차이는 자바스크립트 엔진이 언제 함수를 생성하는지에 있습니다.
함수 표현식은 실제 실행 흐름이 해당 함수에 도달했을 때 함수를 생성합니다. 따라서 실행 흐름이 함수에 도달했을 때부터 해당 함수를 사용할 수 있습니다.
위 예시를 이용해 설명해 보도록 하겠습니다. 스크립트가 실행되고, 실행 흐름이 let sum = function…의 우측(함수 표현식)에 도달 했을때 함수가 생성됩니다. 이때 이후부터 해당 함수를 사용(할당, 호출 등)할 수 있습니다.
하지만 함수 선언문은 조금 다릅니다.
함수 선언문은 함수 선언문이 정의되기 전에도 호출할 수 있습니다.
따라서 전역 함수 선언문은 스크립트 어디에 있느냐에 상관없이 어디에서든 사용할 수 있습니다.
이게 가능한 이유는 자바스크립트의 내부 알고리즘 때문입니다. 자바스크립트는 스크립트를 실행하기 전, 준비단계에서 전역에 선언된 함수 선언문을 찾고, 해당 함수를 생성합니다. 스크립트가 진짜 실행되기 전 "초기화 단계"에서 함수 선언 방식으로 정의한 함수가 생성되는 것이죠.
스크립트는 함수 선언문이 모두 처리된 이후에서야 실행됩니다. 따라서 스크립트 어디서든 함수 선언문으로 선언한 함수에 접근할 수 있는 것입니다.
예시를 살펴봅시다.
*!*
sayHi("John"); // Hello, John
*/!*
function sayHi(name) {
  alert( `Hello, ${name}` );
}
함수 선언문, sayHi는 스크립트 실행 준비 단계에서 생성되기 때문에, 스크립트 내 어디에서든 접근할 수 있습니다.
그러나 함수 표현식으로 정의한 함수는 함수가 선언되기 전에 접근하는 게 불가능합니다.
*!*
sayHi("John"); // error!
*/!*
let sayHi = function(name) {  // (*) 마술은 일어나지 않습니다.
  alert( `Hello, ${name}` );
};
함수 표현식은 실행 흐름이 표현식에 다다랐을 때 만들어집니다. 위 예시에선 (*)로 표시한 줄에 실행 흐름이 도달했을 때 함수가 만들어집니다. 아주 늦죠.
세 번째 차이점은, 스코프입니다.
엄격 모드에서 함수 선언문이 코드 블록 내에 위치하면 해당 함수는 블록 내 어디서든 접근할 수 있습니다. 하지만 블록 밖에서는 함수에 접근하지 못합니다.
예시를 들어 설명해 보겠습니다. 런타임에 그 값을 알 수 있는 변수 age가 있고, 이 변수의 값에 따라 함수 welcome()을 다르게 정의해야 하는 상황입니다. 그리고 함수 welcome()은 나중에 사용해야 하는 상황이라고 가정해 보죠.
함수 선언문을 사용하면 의도한 대로 코드가 동작하지 않습니다.
let age = prompt("나이를 알려주세요.", 18);
// 조건에 따라 함수를 선언함
if (age < 18) {
  function welcome() {
    alert("안녕!");
  }
} else {
  function welcome() {
    alert("안녕하세요!");
  }
}
// 함수를 나중에 호출합니다.
*!*
welcome(); // Error: welcome is not defined
*/!*
함수 선언문은 함수가 선언된 코드 블록 안에서만 유효하기 때문에 이런 에러가 발생합니다.
또 다른 예시를 살펴봅시다.
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에 함수 표현식으로 만든 함수를 할당하면 되죠.
이제 코드가 의도한 대로 동작합니다.
let age = prompt("나이를 알려주세요.", 18);
let welcome;
if (age < 18) {
  welcome = function() {
    alert("안녕!");
  };
} else {
  welcome = function() {
    alert("안녕하세요!");
  };
}
*!*
welcome(); // 제대로 동작합니다.
*/!*
물음표 연산자 ?를 사용하면 위 코드를 좀 더 단순화할 수 있습니다.
let age = prompt("나이를 알려주세요.", 18);
let welcome = (age < 18) ?
  function() { alert("안녕!"); } :
  function() { alert("안녕하세요!"); };
*!*
welcome(); // 제대로 동작합니다.
*/!*
제 경험에 따르면 함수 선언문을 이용해 함수를 선언하는 걸 먼저 고려하는 게 좋습니다. 함수 선언문으로 함수를 정의하면, 함수가 선언되기 전에 호출할 수 있어서 코드 구성을 좀 더 자유롭게 할 수 있습니다.
함수 선언문을 사용하면 가독성도 좋아집니다. 코드에서 `let f = function(…) {…}`보다 `function f(…) {…}` 을 찾는 게 더 쉽죠. 함수 선언 방식이 더 "눈길을 사로잡습니다".
그러나 어떤 이유로 함수 선언 방식이 적합하지 않거나, (위 예제와 같이) 조건에 따라 함수를 선언해야 한다면 함수 표현식을 사용해야 합니다.