본문 바로가기

[모던JS] 020. [기본] 함수 표현식(콜백 함수, 익명 함수)

codeConnection 2024. 3. 30.

원문

함수 표현식 (Function Declaration)이란?

자바스크립트에서는 함수를 생성하는 방법이 두 가지가 있다.

  1. 함수 선언식 (Function Declaration)
  2. 함수 표현식 (Function Expression)
// 함수 선언식
function myFunction() {
    // 함수 body
}

// 함수 표현식
let myFunction = function() {
    // 함수 body
}

함수 선언식은 fuction을 이용해서 함수를 선언하는 것이고, 함수 표현식은 변수를 선언하고 그 변수의 값으로 함수를 초기화 하는 것이다.

그런데 함수 선언식에서는 function 함수명() {함수body} 형태이지만 함수 표현식에서는 함수명이 빠져서 function() {함수body}가 된다. 함수명은 이미 변수명으로 만들었기 때문이다. 여기에서 선언된 함수가 이 변수 그 자체이기 때문에 변수명으로 대체한다.

함수 표현식에서의 함수 호출

함수 표현식으로 함수를 선언해도 기존처럼 함수를 호출할 수 있다.

function myFunction = function() {
    console.log('hello');
}

myFunction(); // hello

console.log(myFunction); // 함수 괄호가 빠진 형태
// 함수의 내용이 출력된다.
// function myFunction = function() {
//     console.log('hello');
// }

console.log(myFunction()); // 'hello', undefined
// 함수의 내용이 console.log인데 이는 콘솔에 함수에서 반환된 값을 출력하는 함수다.
// 그런데 함수 내부에서 console.log는 실행만 되는 함수이지 값을 반환하는 함수가 아니다.
// 따라서 함수가 한 번 실행된 후 값으로 undefined가 반환된다.

함수 표현식에서의 함수 복사

함수 표현식으로 함수를 선언하면 변수에 '값'을 할당하는 것이기 때문에 값을 다른 변수에 복사하듯이 함수를 복사할 수 있다.

function sayHi() {
    alert( 'hello' );
}

let sayHiCopy = sayHi;

sayHi(); // hello
sayHiCopy() // hello

함수를 복사할 때 sayHi를 사용하고 소괄호를 적지 않았는데, 위에서 봤던 예시와도 같다. 소괄호를 빼면 함수 코드 자체가 반환되기 때문에 함수 코드 자체가 변수에 복사되지만,
let sayHiCopy = sayHi(); 처럼 소괄호를 넣게 되면 함수가 실행되고 나서의 반환 값이 복사되게 된다. 이 경우 alert창을 출력하는 함수 외 반환하는 값이 없기 때문에 undefined가 복사되는 것이다.
즉 이 상태에서 sayHiCopy();로 함수를 호출하면 함수가 아니라는 에러가 출력된다.

function sayHi() {
    alert( 'hello' );
}

let sayHiCopy = sayHi;

sayHi();

함수 표현식에 세미콜론이 들어가는 이유

함수 선언식에서는 세미콜론을 넣지 않는다.

function myFunction() {}

그런데 함수 표현식에서는 세미콜론을 넣는다.

let myFunction = function() {};

이유는 중괄호로 만든 코드블럭의 끝엔 세미콜론이 들어가지 않기 때문이다.
if {}, for{}, function() {}

콜백 함수 (callback function)

함수의 인수로 함수가 쓰이는 함수를 콜백 함수라고 한다.

함수는 매개변수를 설정할 수 있다. 함수 매개변수의 역할은 외부에서 값을 받아 함수 내부로 전달할 수 있게 해준다. 그런데 값에 함수를 넣게 되면 함수 내부에서 조건에 따라 특정 동작이 가능하게 할 수도 있다.

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

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

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

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

코드 해석

  1. ask라는 함수를 선언하고 매개변수로 question, yes, no 세 개를 받도록 했다.
  2. 함수의 실행 내용으로는 if문을 사용해서 question 인수로 받은 내용으로 (텍스트가 될 것이다) confirm 대화상자를 연다. 그리고 true면 yes() 함수가 실행되고 false면 else 다음인 no() 함수를 실행한다. confirm 대화상자에서 true는 확인을 누르면 반환되는 값이고 false는 취소를 눌렀을 때 반환되는 값이다.
  3. 그러면 yes(), no()라는 함수를 외부에서 선언해주어야 한다.
  4. showOk() 함수를 선언해서 동의하셨습니다. alert창을 띄우도록 했고 showCancel도 유사하게 선언했다.
  5. 마지막 라인에서 ask()를 통해 함수를 선언했다.
  6. ask 함수를 호출하면서 인수로 alert에 confirm으로 띄우는 question 매개변수를 "동의하십니까?"로 지정하고, 참일 때 실행할 함수를 showOk, 거짓일 때 실행할 함수를 showCancel로 설정했다.

그런데 위 함수를 function() {}과 같은 함수 선언식이 아니라 변수에 값을 담는 함수 표현식으로 작성한다면 코드를 더 간결하게 작성할 수 있다.

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

ask(
    '동의하십니까?',

    function() {
        alert('동의하셨습니다.');
    },

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

익명 함수 (anonymous function)

위 함수를 보면 ask 안에 함수가 선언되어 있다. 그런데 함수 이름이 없다. 이것을 익명 함수라 한다. 이 함수들은 별도로 외부에서 function을 통해 선언된 것이 아니기 때문에 외부에서 호출 할 수 없고 ask 내부에서만 접근할 수 있다. 따라서 외부에서 호출할 함수명이 필요가 없는 것이다. 그래서 이렇게 익명 함수로 작성한다.

함수 선언문 vs 함수 표현식 비교

함수 선언문과 함수 표현식은 둘 다 함수를 만드는 방법이다. 그런데 어떤 것을 사용해야 하느냐는 약간의 차이로 갈리지만 처음부터 이 모든 차이를 고려하긴 어렵고, 일반적으로는 함수 선언식을 먼저 고려하는 것이 가독성 면에서도 직관적이고, 함수를 선언하기도 전에 호출할 수도 있기 때문에 장점이 조금 더 많다.

문법적 차이

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

함수 생성 시기 차이

  • 함수 선언문
    • 함수 선언문은 어디에서든 함수를 호출할 수 있다.
    • 즉 함수를 선언하기 전, 위의 코드 라인에서도 함수를 호출할 수 있다는 것이다.
  • 함수 표현식
    • 함수 표현식은 코드를 쭉 실행하다 let sum = function...에 흐름이 도달했을 때 함수가 생성된다.
    • 즉 함수를 호출할 수 있는 시기는 함수가 선언된 이후부터 호출이 가능하다. 함수가 정의되지도 않았는데 함수를 호출할 수 없다.
// 함수 선언식
sayHi('장원영');

function sayHi(name) {
    alert(`Hello, ${name}`);
}
// 함수 선언 전 함수 호출 가능

// 함수 표현식
sayHi('장원영');

let sayHi(name) {
    alert(`Hello, ${name}`);
}
// 에러 발생, 함수 선언 전 호출 불가능

스코프 차이

  • 함수 선언문
    • 함수 선언문으로 선언된 함수가 다른 코드의 안에 있을 때는 그 코드블럭 안에서만 호출이 가능하다.
  • 함수 표현식
    • 함수 표현식으로 선언된 함수는 그 함수가 선언된 코드블럭 밖에서도 호출이 가능하다.
let age = 16;

if (age < 18>) {
    welcome(); // 실행됨

    function welcome() {
        alert('안녕');
    }
} else {
    function welcome() {
        alert('안녕하세요');
    }
}

welcome(); // 실행안됨
// 중괄호 밖에서는 코드블럭 내부에서 함수 선언문으로 선언된 함수를 호출할 수 없음.
let age = prompt('나이를 알려주세요', 18)

let welcome;

if (age < 18) {
    welcome = function() {
        alert('안녕');
    };
} else {
    welcome = function() {
        alert('안녕하세요');
    };
}

welcome; // 실행됨

그리고 물음표 연산자로 위 코드를 간결하게 할 수 있다. 물음표 함수는 (condition) ? a : b 형태로 쓰며 condition이 true이면 a를 반환하고 false이면 b를 반환한다.

let age = prompt('나이를 알려주세요', 18)

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

welcome(); // 실행됨

댓글