JavaScript/Basics

[JavaScript] 함수 표현식

Sonny Cucumber 2022. 3. 10. 16:16

JS에서는 함수를 특별한 종류의 으로 취급합니다 다른 언어처럼

특별한 동작을 하는 구조로 취급안됨.

 

보통은 함수 선언문 방식으로 함수를 만들지만

이 외에 함수 표현식을 사용해서 만들 수도 있다. (Function Expression)

 

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

변수에 값을 할당하는 것처럼 함수가 할당 됨.

 

그래서 괄호가 없이 이렇게 sayHi만 그대로 넣으면 함수가 실행이 안되고 함수 안의

소스코드가 그대로 alert으로 출력이 된다.

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

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

이 외에 함수의 값을 변수에 할당해줄 수 있는데

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

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

func(); // Hello     // (3) 복사한 함수를 실행(정상적으로 실행됩니다)!
sayHi(); // Hello    //     본래 함수도 정상적으로 실행됩니다.
  1. 1에선 똑같이 함수선언을 하여 생성한 후 sayHi에 저장이 되고
  2. 2에선 func 변수에 sayHi를 복사한다. 중요한 것은 sayHi에 괄호가 없는 점. 
    괄호가 있었다면 func=sayHi()가 되어 sayHi함수 그 자체가 아니라, 함수 호출 결과가 저장될 것. 
    위 코드에선 let func = sayHi();가 되어버리면 바로 실행이 됨.
  3. sayHi()와 func()로 함수를 호출할 수 있게 됨 

콜백 함수

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

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

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

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

showOk, showCancel처럼 콜백 함수 또는 콜백이라고 불리는데

함수를 함수의 인자로 전달하고, 필요하다면 인수로 전달한 그 함수를 "나중에 호출(called back)"하는 것이 콜백

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

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

이런 식으로 ask안의 함수가 선언이 된다면 코드의 길이가 짧아지며

이렇게 선언한 함수를 익명함수 (anonymous function)이라고도 부름. 이런 함수는

변수에 할당된게 아니기에 외부에서 접근이 안됨.

 

함수 표현식 vs 함수 선언문

  • 함수 선언문 : 함수는 주요 코드 흐름 중간에 독자적인 구문 형태로 존재
  • 함수 표현식: 함수는 표현식이나 구문 구성(syntax construct) 내부에 생성됩니다. 아래 예시에선
    함수가 할당 연산자 = 를 이용해 만든 "할당 표현식" 우측에 생성되었습니다.
// 함수 선언문
function sum(a, b) {
  return a + b;
}

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

다음 차이는 JS엔진이 언제 함수를 생성하는지에 있다

함수 표현식 (function expression)은 실제 실행 흐름이 해당 함수에 도달했을 때 함수를 생성합니다.

따라서 실행 흐르밍 함수에 도달했을 때부터 해당 함수를 사용할 수 있습니다.

 

그러나 함수 선언문은 조금 다름. 이는 정의되기 전에도 호출 할 수 있음

즉, 전역 함수 선언문은 스크립트 어디에서나 사용이 가능. 이유는 내부 알고리즘 때문인데,

스크립트를 실행하기 전, 준비단계에서 전역에 선언된 함수 선언문을 찾고, 해당 함수를 생성한다.

스크립트가 진짜 실행되기 전 "초기화 단계"에서 함수 선언 방식으로 정의한 함수가 생성되는 것.

그래서 함수 선언문(function declaration)이 모두 처리된 이후에야 실행됨. 그래서 어디서든 접근이 가능

 

세번째 차이는 스코프인데

strict mode에서 함수선언문이 코드 블록 내에 위치하면 이 안에선 접근이 가능하지만

블록 밖에서는 접근이 안된다. 

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

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

} else {

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

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

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

하지만 함수 표현식 (function Expression)으로 만든 함수를 할당하게 된다면 가능하다

이를 삼항연산자 ?를 사용하면 좀 더 쉽게 단순화 가능

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(); // 제대로 동작합니다.

 

둘 중에 무엇을 선택해야할까?

먼저 함수 선언문을 이용하는게 좋다. 이렇게 정의하면 함수가 선언되기 전에 호출할 수 있어서

코드 구성에 자유롭고 가독성도 좋다. 이외에, 조건에 따라 함수를 선언해야 한다면 함수표현식을 고려