[모던 JavaScript 튜토리얼] 2.16 함수 표현식

모던 JavaScript 튜토리얼을 읽고 정리하였습니다.

함수 표현식

자바스크립트는 함수를 특별한 종류의 값으로 취급한다. 이전 챕터에서는 함수 선언(Function Declaration) 방식으로 함수를 만들었지만 함수 표현식(Function Expression)을 사용해서 함수를 만들 수도 있다.

함수표현식으로 함수를 생성해보자.

let sayHi = fuction() {
  alert("Hello");
};

함수를 생성하고 변수에 값을 할당하는 것처럼 함수가 변수 sayHi에 저장된 값이 되었다. 함수가 어떤 방식으로 만들어졌는지에 관계없이 함수는 값이므로 변수에 할당 가능하다.

함수는 값이기 때문에 alert를 이용하여 함수 코드를 출력할 수도 있다. 아래 예시에서 마지막 줄에 sayHi 옆에 괄호가 없으므로 함수가 실행되는 것이 아니라, 함수의 소스 코드문자형으로 바뀌어 반환된다.

function sayHi() {
  alert(Hello);
}
alert(sayHi);

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

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

let func = sayHi; 

func(); // 복사한 함수를 실행
sayHi();

위 예시의 동작 원리는 다음과 같다.

  1. 함수 선언 방식을 통해 함수를 생성하고, sayHi라는 변수에 저장된다.
  2. sayHi를 새로운 변수 func에 복사한다. sayHi 다음에 괄호가 없으므로 함수의 반환값이 아니라 함수 그 자체가 복사된다.
  3. 이젠 sayHi()와 func()로 함수를 호출이 가능하다.

끝에 세미콜론이 있는 이유

함수 선언문과 달리 함수 표현식의 끝에 세미콜론이 붙은 이유는 let sayHi = …;과 같은 구문에서 함수표현식이 코드 블록이 아니라 값처럼 취급되기 때문이다. 따라서 세미콜론은 함수표현식 때문에 붙여진 게 아니라, 구문의 끝이라서 붙여진 것이다.


콜백 함수

함수 표현식에 대한 예시를 좀 더 살펴보자. 매개변수 3개를 갖는 함수, ask(questionm, yes, no)를 작성해보자. 각 매개변수에 대한 설명은 아래와 같다.

  • question 질문
  • yes “Yes”라고 답한 경우 실행되는 함수
  • no “No”라고 답한 경우 실행되는 함수
function ask(question, yes, no) {
  if (confirm(question)) yes()
  else no();
}

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

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

ask("동의하십니까?", showOk, showCancel);

이렇게 함수를 작성하는 방법은 실무에서 아주 유용하게 쓰인다.

함수 ask의 인수, showOkshowCancel콜백 함수 또는 콜백이라고 불린다. 콜백 함수함수를 함수의 인수로 전달하고, 필요할 때 인수로 전달한 함수를 나중에 호출(called back)하는 것을 말한다. 위 예시에서는 사용자가 yes라고 대답한 경우 showOk가 콜백이 되고, no라고 대답한 경우 showCancel이 콜백이 된다.

아래와 같이 함수 표현식을 사용하면 코드 길이가 짧아진다. 다음과 같이 이름 없이 선언한 삼수는 익명 함수(anonymous function)라고 부른다. 익명 함수는 변수에 할당한게 아니므로, ask 바깥에선 접근할 수 없다.

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

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

함수는 동작을 나타내는 값

문자열이나 숫자 등의 일반적인 값들은 데이터를 나타낸다. 함수는 하나의 동작을 나타낸다. 동작을 대변하는 값인 함수를 변수 간 전달하는 것이다.


함수 표현식 vs 함수 선언문

함수 표현식과 선언문의 차이는 다음과 같다.

문법

  • 함수 선언문 : 함수는 주요 코드 흐름 중간에 독자적인 구문 형태로 존재한다.
function sum(a, b) {
  return a + b;
}
  • 함수 표현식: 함수는 표현식이나 구문 구성(syntax construct) 내부에 생성된다.
let sum = function(a, b) {
  return a + b;
}

함수 생성 시점

  • 함수 표현식 : 실제 실행 흐름이 해당 함수에 도달했을 때 함수가 생성된다. 따라서 함수가 정의된 코드 밑 줄부터 함수를 호출할 수 있다.
sayHi("John"); // error!
let sayHi = function(name) {
  alert('Hello, ${name}');
};
  • 함수 선언문 : 함수 선언문이 정의되기 전에도 호출할 수 있다. 이게 가능한 이유는 자바스크립트 내부 알고리즘 때문이다. 자바스크립트는 스크립트를 실행하기 전, 준비 단계에서 전역에 선언된 함수 선언문을 찾고 해당 함수를 생성한다. 스크립트가 진짜 실행되기 전 초기화 단계에서 함수 선언 방식으로 정의한 함수가 생성되는 것이다.
sayHi("John");

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

스코프

  • 함수 선언문 : 코드 블록 내에 위치하면 해당 함수는 블록 내 어디서든 접근할 수 있다. 하지만 블록 밖에서는 함수에 접근하지 못한다.
let age = prompt("나이를 알려주세요.", 18);

if (age < 18) {
  function welcome() {
    alert("안녕!");
  } 
} 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();

함수 선언문 vs 함수 표현식

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

삼수 선언문을 사용하면 가독성도 좋다. let f = function(…) {…}보다 function f(…) […]을 찾는게 더 쉽다.

Comments