자바스크립트는 함수를 특별한 종류의 값으로 취급한다. 다른 언어에서처럼 "특별한 동작을 하는 구조"로 취급되지 않는다.
이전 챕터에서 다룬 함수 선언 방식 외에 함수 표현식(Function Expression)을 사용해서 함수를 만들 수 있다.
함수를 생성하고 변수에 값을 할당하는 것처럼 함수가 변수에 할당되었다. 함수가 어떤 방식으로 만들어졌는지에 관계없이 함수는 값이고, 따라서 변수에 할당할 수 있다. 위 예시에선 함수가 변수 sayHi에 저장된 값이 되었다.
위 예시를 간단한 말로 풀면, "함수를 만들고 그 함수를 변수 sayHi에 할당"하는 예시이다.
자바스크립트에서 함수는 값이기 때문에 alert를 이용하여 함수 코드를 출력할 수도 있다. 마지막 줄에서 sayHi옆에 괄호가 없기 때문에 함수는 실행되지 않는다.
자바스크립트는 괄호가 있어야만 함수가 호출된다.
함수는 sayHi()처럼 호출할 수 있다는 점에서 일반적인 값과는 조금 다르다. 하지만 어쨌든 본질은 값이기 때문에 값에 할 수 있는 일을 함수에도 할 수 있다.
예를 들면, 변수를 복사해 다른 변수에 할당하는 것처럼 함수를 복사해 다른 변수에 할당할 수도 있다.
함수 sayHi는 아래와 같이 함수 표현식을 사용해 정의할 수 있다. 동작 결과는 동일하다. 다만 위와 같이 정의 시, 끝에 세미콜론 붙여주는 것을 잊지 말자.
함수를 값처럼 전달하는 예시, 함수 표현식에 관한 예시를 좀 더 살펴보자.
매개변수가 3개 있는 함수, ask(question, yes, no)를 작성해보겠다. 각 매개변수에 대한 설명은 아래와 같다.
함수는 반드시 question(질문)을 해야 하고, 사용자의 답변에 따라 yes() 나 no()를 호출한다. 함수 ask의 인수, showOk와 showCancel은 콜백 함수 또는 콜백이라고 불린다.
💡 콜백 함수 : 함수를 함수의 인수로 전달하고, 필요하다면 인수로 전달한 그 함수를 "나중에 호출(called back)"하는 것
아래와 같이 함수 표현식을 사용하면 코드 길이가 짧아진다.ask(...) 안에 함수가 선언되어 있다. 이렇게 이름 없이 선언한 함수는 익명 함수(anonymous function) 라고 부른다. 익명 함수는 (변수에 할당된 게 아니기 때문에) ask 바깥에선 접근할 수 없다.
자바스크립트를 사용하다 보면 콜백을 활용한 코드를 아주 자연스레 만나게 된다. 이런 코드는 자바스크립트의 정신을 대변한다.
💡 함수는 "동작"을 나타내는 값!
문자열이나 숫자 등의 일반적인 값들은 데이터를 나타낸다.
함수는 하나의 동작(action)을 나타낸다.
동작을 대변하는 값인 함수를 변수 간 전달하고, 동작이 필요할 때 이 값을 실행할 수 있다.
그렇다면, 함수 표현식과 함수 선언문의 차이는 무엇일까?
함수는 주요 코드 흐름 중간에 독자적인 구문 형태로 존재한다.
함수는 표현식이나 구문 구성(syntax construct) 내부에 생성된다. 아래 예시에선 함수가 할당 연산자 =를 이용해 만든 "할당 표현식" 우측에 생성되었다.
함수 선언문이 정의되기 전에 호출할 수 있다. 따라서 전역 함수 선언문은 스크립트 어디에 있느냐에 상관없이 어디에서든 사용할 수 있다.
🤷🏻♂️ 이게 어떻게 가능해?
이게 가능한 이유는 자바스크립트의 내부 알고리즘 때문이다. 자바스크립트는 스크립트를 실행하기 전, 준비단계에서 전역에 선언된 함수 선언문을 찾고, 해당 함수를 생성한다. 스크립트가 진짜 실행되기 전 "초기화 단계"에서 함수 선언 방식으로 정의한 함수가 생성되는 것이다.
스크립트는 함수 선언문이 모두 처리된 이후에서야 실행된다. 따라서 스크립트 어디서든 함수 선언문으로 선언한 함수에 접근할 수 있는 것이다.
함수 선언문, sayHi는 스크립트 실행 준비 단계에서 생성되기 때문에 스크립트 내 어디에서든 접근할 수 있다.
실제 실행 흐름이 해당 함수에 도달했을 때 함수를 생성한다. 따라서 실행 흐름이 함수에 도달했을 때부터 해당함수를 사용할 수 있다.
함수가 선언되기 전에 접근하는 게 불가능하다.함수 표현식은 실행 흐름이 표현식에 다다랐을 때 만들어진다.
엄격 모드에서 함수 선언문이 코드 블록 내에 위치하면 해당 함수는 블록 내 어디서든 접근할 수 있다. 하지만 블록 밖에서는 함수에 접근하지 못한다.
예시를 들어보자. 런타임에 그 값을 알 수 있는 변수 age가 있고, 이 변수의 값에 따라 함수 welcome()을 다르게 정의해야 하는 상황이다. 그리고 함수 welcome()은 나중에 사용해야 하는 상황이라고 가정해보자.
의도한 대로 코드가 동작하지 않는다.함수 선언문은 함수가 선언된 코드 블록 안에서만 유효하기 때문에 이런 에러가 발생한다. 다른 코드로 살펴보자.
그럼 if문 밖에서 welcome 함수를 호출할 방법은 없는 걸까?
if문 밖에 선언한 변수 welcome에 함수 표현식으로 만든 함수를 할당하면 된다.이제 코드가 의도대로 실행된다.
물음표 연산자 ?를 사용하면 위 코드를 좀 더 단순화할 수 있다.
🤷🏻♂️ 그럼, 함수 선언문이랑 함수 표현식 중 무엇을 선택해야할까?
https://ko.javascript.info/ 의 필자의 경험에 의하면, 함수 선언문을 이용해 함수를 선언하는 걸 먼저 고려하는 것이 좋다고 한다. 함수 선언문으로 함수를 정의하면, 함수가 선언되기 전에 호출할 수 있어서 코드 구성을 좀 더 자유롭게 할 수 있다. 또한 함수 선언문을 사용하면 가독성도 좋아진다.
그러나,
어떤 이유로 함수 선언 방식이 적합하지 않거나, (위 예제와 같이) 조건에 따라 함수를 선언해야 한다면 함수 표현식을 사용해야한다.
이 글은 https://ko.javascript.info/ 를 참고하여 작성하였습니다.