이 글에서는 먼저 함수를 사용하는 이유를 알아볼 것이다. 그 후, 함수 객체를 생성하는 방법으로 함수 리터럴과 네 가지 함수 정의 방식을 알아볼 것이다. 그리고 매개변수 전달 방식에 따른 함수 외부 상태 변화, 다양한 함수의 형태 등 함수를 다방면에서 알아볼 것이다.
함수를 사용하는 이유
어떤 특정한 기능을 하는 코드 뭉치를 모아두고 이름을 붙인 것이 함수이다. 매번 모든 코드를 작성하지 않고 이름 붙여분 함수를 호출하는 것으로 대신하면, 다음과 같은 장점이 있다.
- 유지보수 편의성 증가
코드 뭉치의 일부 로직을 수정하고자 할 때, 매번 직접 작성한 경우 모든 코드 뭉치의 해당 로직을 수정해야 하지만, 함수를 이용하면 함수의 로직만 수정해도 모두에게 같은 수정이 적용된다.
- 코드의 신뢰성 증가
각각을 직접 수정할 때 발생하는 누락이나 오타 등의 실수를 방지할 수 있다.
- 코드의 가독성 향상
코드 뭉치가 어떤 일을 하는지 함수의 이름으로 알려줄 수 있다. 적절한 함수 이름을 지으면, 최소한의 설명으로도 함수가 하는 일을 파악하고 사용할 수 있다.
함수 리터럴
리터럴은 미리 약속해 둔 기호로 값을 생성하는 표기법이라고 했다. 숫자 값으로 숫자 리터럴을 생성하고, 문자열로 문자열 리터럴을 생성하듯이, 함수도 함수 리터럴로 생성할 수 있다. 함수 리터럴은 function 키워드, 함수 이름, 매개변수 목록, 함수 몸체로 구성된다.
| 키워드 | 이름 |매개변수|
const f = function add (x, y) {
return x + y; | 몸체
};
- 함수 이름
- 식별자이므로 네이밍 규칙 준수해야 함. 함수 이름 생략 가능
- 함수 몸체 내에서만 참조 가능 → 함수 외부에서 참조 불가
- 매개변수 목록
- 함수 몸체 내에서 변수와 동일한 취급. 따라서 식별자 네이밍 규칙 준수해야 함
- 함수 몸체
- 함수 호출은 표현식이며, 반환값이 함수호출의 평가 결과가 된다.
반환문이 없는 경우 undefined가 반환된다.
함수 정의
| 함수 선언문 | 함수 표현식 |
|---|
| 코드 | function add(x, y) { | const add = function (x, y) { |
| return x + y; | return x + y; |
| } | }; |
| 함수 이름 생략 | O | X |
| 표현식 여부 | X | O |
| 함수 호이스팅 | O | X |
함수 선언문
- vs 기명 함수 리터럴: 중의적인 코드이나, 문맥에 따라 해석된다.
- 기명 함수 리터럴: 변수에 할당되거나 피연산자로 사용될 때 → 외부에서 함수 이름 참조 불가
- 함수 선언문: 그 외 → 외부에서 함수 이름 참조 가능해 보임
자바스크립트 엔진이 함수 이름과 동일한 이름의 식별자를 암묵적으로 생성하고 거기에 함수 객체 할당
[중요] 함수는 이름으로 호출하는 것이 아니라 함수 객체를 가리키는 식별자로 호출
- 함수 호이스팅이 동작함
- 함수 선언문이 코드의 선두로 끌어 올려진 것처럼 동작하는 자바스크립트의 특징
- 선언문은 런타임 이전에 평가되며, 함수 객체가 생성되고 암묵적 식별자에 할당되는 과정이 일어난다.
→ 런타임에 실행하는 나머지 문이 어느 위치에서 함수를 호출하든 호출 가능하다.
함수 표현식
- 함수 호이스팅이 동작하지 않음
- 런타임, 할당문이 실행되는 시점에 평가되고, 이때 함수 객체가 생성되고 선언된 변수에 할당된다.
→ 함수 할당 이후에야 함수를 호출할 수 있다.
[중요] 함수 정의 이후에 함수를 호출하는 것이 당연하므로, 함수 선언문보다 함수 표현식을 사용할 것을 권장
Function 생성자 함수
let add = new Function('x', 'y', 'return x + y');
- 매개변수 목록과 함수 몸체를 문자열로 전달
- 함수 생성 방식이 일반적이지 않으며, 생성된 함수의 동작도 함수 선언문이나 함수 표현식과 다르다.
화살표 함수
const add = (x, y) => x + y;
- 간략하게 함수를 정의하는 방법으로 항상 익명 함수로 정의한다.
- 표현 뿐 아니라 내부 동작 또한 간략화되어 있다.
this 바인딩 방식이 다르다.
prototype 프로퍼티 존재하지 않는다.
arguments 객체 생성하지 않는다.
매개변수 전달 방식에 따른 외부 상태 변화
값에 의한 전달(호출)
- 원시값은 값이 직접 복사되어 매개변수에 전달된다.
- 원시값은 변경 불가능 값으로, 매개변수 재할당은 외부 상태에 영향을 주지 않는다.
참조에 의한 전달(호출)
- 객체는 참조값이 복사되어 매개변수에 전달된다.
- 객체는 변경 가능한 값으로, 매개변수로 받은 객체를 직접 변경할 경우 외부에 있는 원본 객체도 변경된다.
- 함수에 의해 외부 상태가 변경되는 것은 혼란을 야기할 수 있다.
→ 변경하려는 객체를 깊은 복사 후 변경하면 함수 외부 상태에 영향을 미치지 않을 수 있다.
부수 효과가 없는(동일한 인수가 오면 동일한 값 반환, 외부 상태 변화 X) 함수를 순수 함수라고 한다
다양한 함수의 형태
즉시 실행 함수
(function () {
const a = 3;
const b = 5;
return a + b;
})();
- 기명/무기명 모두 가능하나, 한 번만 호출되며 다시 호출할 수 없다.
- 초기화 작업이나, 변수나 함수의 이름 충돌을 방지할 때 사용한다.
재귀함수
function countdown(n) {
if (n < 0) return;
console.log(n);
countdown(n-1);
}
- 자기 자신을 호출하는 함수
- 반복문 없이 반복되는 처리를 하기 위해 사용
- 종료 조건이 필수적이다. (무한 루프 조심)
중첩 함수
- 함수 내부에 정의된 함수
- 외부 함수 내부에서만 호출될 수 있으며, 외부 함수를 돕는 헬퍼 함수 역할을 한다.
- 호이스팅 방지를 위해 함수 선언문은 사용하지 말아야 한다.
콜백 함수
- 매개 변수로서 다른 함수의 내부로 전달되는 함수
- vs 중첩 함수: 전달되는 함수 내부에만 고정되어 있지 않다.
- 비동기 처리, 배열의 고차함수 등에서 많이 이용된다.
[참고]