함수 표현식

함수 표현식

이전 챕터에서는 함수 선언(Function Declaration), 함수 선언문 방식으로 함수를 만들었다.

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

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

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

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

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

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

function sayHi() { alert('Hello'); } alert(sayHi); // 함수 코드가 보임

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

자바스크립트에서 함수는 값이다. 따라서 함수를 값처럼 취급할 수 있다. 위 코드에서는 함수 코드가 문자형으로 바뀌어 출력된다.

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

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

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

function sayHi() { // (1) 함수 생성 alert("Hello"); } let func = sayHi; // (2) 함수 복사 func(); // (3) 복사된 함수를 실행, Hello sayHi(); // 본래의 함수도 정상적으로 실행됨, Hello

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

함수 sayHi 는 아래와 같이 함수 표현식을 사용해 정의할 수 있고, 동일하게 동작한다.

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

✔ 끝에 세미콜론은 왜 있을까?

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

이유는 간단하다.

- if { ... }, for { }, function f { } 같이 중괄호로 만든 코드 끝에는 세미콜론이 없어도 된다.

- 함수 표현식은 let sayHi = ... ; 과 같은 구문 안에서 값의 역할을 한다. 코드 블록이 아니고 값처럼 취급되어 변수에 할당된다. 모든 구문의 끝에는 세미콜론을 붙이는 게 좋다.

함수 표현식에 쓰인 세미콜론은 함수 표현식 때문에 붙여진 게 아니라, 구문의 끝이기 때문에 붙여졌다.

콜백 함수

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

question - 질문

yes - "Yes" 라고 답한 경우 실행되는 함수

no - "No" 라고 답한 경우 실행되는 함수

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

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("취소 버튼을 누르셨습니다."); } );

ask( ... )안에 선언된 함수처럼 이름 없이 선언된 함수를 익명함수(anonymous function)라고 부른다.

익명 함수는 변수에 할당된 게 아니기 때문에 ask 바깥에서는 접근할 수 없다.

✔ 함수는 "동작" 을 나타내는 값이다.

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

함수는 하나의 동작(action)을 나타내는다.

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

함수 표현식 vs 함수 선언문

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

// 함수 선언문 function sum(a, b) { return a + b; }

함수 표현식: 함수는 표현식이나 구문 구성(syntax construct)내부에 생성된다.

아래 예시에서는 함수가 할당 연산자 = 를 이용해 만든 "할당 표현식" 우측에 생성되었다.

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

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

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

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

하지만 함수 선언문은 함수 선언문이 정의되기 전에도 호출할 수 있다.

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

이게 가능한 이유는 자바스크립트의 내부 알고리즘 때문이다. 자바스크립트는 스크립트를 실행하기 전, 준비단계에서 전역에 선언된 함수 선언문을 찾고, 해당 함수를 생성한다.

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

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

sayHi("John"); // Hello, John fucntion sayHi(name) { alert(`Hello ${name}`); }

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

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

sayHi("Jonh"); // 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; 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( ... ) { ... } 을 찾는게 더 쉽다.

함수 선언 방식이 더 눈길을 사로잡는다.

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

요약

함수는 값이다. 따라서 함수도 값처럼 할당, 복사, 선언할 수 있다.

함수 선언문 방식으로 함수를 생성하면, 함수가 독립된 구문 형태로 존재하게 되며, 코드 블록이 실행되기도 전에 처리되기 때문에 블록 내 어디서든 활용이 가능하다.

함수 표현식 방식으로 함수를 생성하면, 함수가 표현식의 일부로 존재하게 되며, 실행 흐름이 표현식에 다다랐을 때 만들어진다

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

함수 표현식은 함수 선언문을 사용하는 게 부적절할 때에 사용하는 것이 좋다.

출처 : 모던자바스크립트 튜토리얼

from http://heimish-web.tistory.com/62 by ccl(A) rewrite - 2021-11-19 16:02:32