자바스크립트에서 함수는 호출 가능한 행동 객체로, 객체의 일종이다.
따라서 함수에도 객체와 동일하게 프로퍼티 추가/제거 및 참조 전달이 가능하다.
function sayHi() {
console.log("Hi");
}
console.log(sayHi.name); // sayHi
// 배열 안에서 함수를 생성함
let arr = [function() {}];
console.log( arr[0].name ); // <빈 문자열>
function f1(a) {}
function f2(a, b) {}
function many(a, b, ...more) {}
console.log(f1.length); // 1
console.log(f2.length); // 2
console.log(many.length); // 2
function ask(question, ...handlers) {
let isYes = confirm(question);
for(let handler of handlers) {
if (handler.length == 0) {
if (isYes) handler();
} else {
handler(isYes);
}
}
}
// 사용자가 OK를 클릭한 경우, 핸들러 두 개를 모두 호출함
// 사용자가 Cancel을 클릭한 경우, 두 번째 핸들러만 호출함
ask("질문 있으신가요?", () => alert('OK를 선택하셨습니다.'), result => alert(result));
우선 코드 실행 과정부터 보자.
ask 함수의 인수로
() => alert('OK를 선택하셨습니다.')
result => alert(result)
를 전달한다.
첫번째 인수는 question
매개변수에 담기게 되고,
나머지 인수들은 handlers
라는 함수를 요소로 가지는 배열에 담기게 된다.
question
의 값으로 사용자에게 확인 창을 띄우고, 사용자의 응답을 isYes
에 담는다.
js에서 confirm 메소드는 확인창을 띄우는 역할이다. 사용자가 OK 버튼을 누를 경우 true, 취소 버튼을 누를 경우 false를 반환한다.
이후 handlers
배열에 담긴, 전달받은 모든 함수를 순회하면서
이 코드 실행은 결국 이렇게 해석할 수 있다.
이렇게 length의 값, 즉 인수의 개수에 따라 함수를 다르게 실행할 수 있는 JS의 성질을 다형성 이라고 한다.
함수라는 객체에 프로퍼티를 추가하는 것도 가능하다.
function sayHi() {
alert("Hi");
// 함수를 몇 번 호출했는지 세봅시다.
sayHi.counter++;
}
sayHi.counter = 0; // 초깃값
sayHi(); // Hi
sayHi(); // Hi
alert( `호출 횟수: ${sayHi.counter}회` ); // 호출 횟수: 2회
여기서 주의해야할 점은
추가한 함수의 프로퍼티는 변수가 아니다!
즉 sayHi.counter = 0
으로 프로퍼티를 추가한 것이
let counter = 0
과 같은 일을 한 것이 절대! 아니다.
커스텀 프로퍼티를 통해 클로저를 대체할 수도 있다.
function makeCounter() {
// let count = 0 대신 아래 메서드(프로퍼티)를 사용함
function counter() {
return counter.count++;
};
counter.count = 0;
return counter;
}
let counter = makeCounter();
alert( counter() ); // 0
alert( counter() ); // 1
두 방법의 차이점은 count 값이 외부 변수에 저장되어있는 경우 드러납니다. 클로저를 사용한 경우엔 외부 코드에서 count에 접근할 수 없습니다. 오직 중첩함수 내에서만 count 값을 수정할 수 있습니다. 반면 함수 프로퍼티를 사용해 count를 함수에 바인딩시킨 경우엔 다음 예시와 같이 외부에서 값을 수정할 수 있습니다.
count
가 외부 변수값으로 저장되었을 때 기명 함수 표현식 : 이름이 있는 함수 표현식
let sayHi = function(who) {
console.log(Hello, ${who});
};
기존에 익숙했던 위의 형태에
let sayHi = function func(who) {
console.log(Hello, ${who});
};
func
이라는 이름을 붙여주는 방식이다.
이러한 기명 함수 표현식을 활용하면 다음이 가능하다.
let sayHi = function func(who) {
if (who) {
console.log(`Hello, ${who}`);
} else {
func("Guest"); // func를 사용해서 자신을 호출합니다.
}
};
func
이 아닌 sayHi
를 통해 자신을 호출하는 것도 물론 가능하지만
sayHi
는 외부에서 null이 할당되는 등 변경될 가능성이 있기 때문에
에러 방지 차원에서 func라는 이름을 사용하는 것이 좋다.
다만, 이런 기명 함수 표현식은 말그대로 함수 표현식에서만 사용이 가능하고, 함수 선언문에서는 사용할 수 없다.
따라서 함수 선언문을 사용하다가도 내부 이름을 지정해줄 필요가 생길 경우에 함수 표현식으로 바꿔주면 된다!