JavaScript의 함수는 함수이면서 동시에 객체이기도 하다는 사실을 이미 알고 있다.
하지만 한 번 더 짚고 넘어가보자!😣
var func = function(){
return 1;
};
func.prop = 'i\'m property!';
func.method = function(){
console.log('I\'m method!');
};
console.log(func()); // 1
console.log(func.prop); // I'm property!
func.method(); // I'm method!
console.dir(func);
위의 코드는 함수도 객체임을 잘 보여주고 있다.
코드를 보면 함수 func는 호출 시 단순히 1을 return 해주는, 함수 표현식으로 정의한 함수이지만 동적으로 prop
와 method
프로퍼티를 추가해주었고 정상적으로 호출도 가능하다.
그렇다면 console.dir(func)
을 이용해 함수 객체의 프로퍼티를 확인하면 어떤 결과가 나올까?
직접 추가해준 method
와 prop
말고도 아래에 arguments
, caller
, length
, name
, prototype
, __proto__
같이 여러 가지 프로퍼티들이 보인다.
이것이 함수 객체가 가지고 있는 기본 프로퍼티이다. 각자 어떤 역할을 하는지 한 번 알아보자.
caller
와 arguments.callee
모두 strict mode에서 deprecated 된 것 같아서 제외하겠다..😥
var func1 = function(a){};
var func2 = function(a, b){};
var func3 = function(a, b, c){};
console.log(func1.length); // 1
console.log(func2.length); // 2
console.log(func3.length); // 3
length
프로퍼티는 함수를 선언할 때 정의된 매개변수의 수를 나타낸다.
이후 설명할 arguments
객체의 length
와 헷갈릴 수 있으니 주의하자.
function func1(a){}
var func2 = function(a, b){};
var funcfunc = function func3(a, b, c){};
console.log(function(){}.name); // ""
console.log(func1.name); // func1
console.log(func2.name); // func2
console.log(funcfunc.name); // func3
name
프로퍼티는 선언한 함수의 함수명을 담고 있는 프로퍼티인데, 상황마다 저장되는 값이 다르다.
다음을 참고하자.
익명 함수의 경우 name
은 null
이나 undifined
가 아닌 ""
가 된다. 즉, Stirng 타입으로 저장되고
함수 선언문은 정의한 함수명 그대로 저장된다.
함수 표현식의 경우 함수명을 따로 정의하지 않으면 name
프로퍼티에는 변수명이 저장된다.
함수명을 정의해준 경우에는 변수명을 무시하고 함수명이 그대로 저장된다.
arguments
는 함수 호출 시 전달 인자와 함께 함수 내부로 전달되는 유사 배열 객체이다.
유사 배열 객체는 위와 같이 배열 객체처럼 프로퍼티의 key가 배열의 index, value가 요소의 데이터를 담고 있고, length
프로퍼티를 가지고 있지만, 배열의 메서드는 사용할 수 없는 객체를 말한다.
call()
과 apply()
를 이용하면 배열의 메서드를 사용할 수 있지만 이는 다음에 포스팅하겠다.
이러한 arguments
객체는 왜 필요한가? 아래의 코드를 한번 보자.
var multipli = function(a, b, c){
console.log(a, b, c);
return a*b*c;
};
multipli(1,3,5); // Parameter : 1 3 5, return : 15
multipli(1,3); // Parameter : 1 3 undefiend, return : NaN
multipli(1); // Parameter : 1 undefiend undefiend, return : NaN
multipli(); // Parameter : undefiend undefiend undefiend, return : NaN
multipli(1,3,5,6); // Parameter : 1 3 5, return : 15
JavaScript는 위와 같이 함수에 정의된 매개변수보다 전달 인자의 수가 적거나 많아도 에러를 일으키지 않고, 전달 인자의 수가 매개변수보다 적을 경우 undefiend
로 처리된다.
따라서 함수 호출 시 전달 인자의 수를 확인하고 이에 따라 동작을 다르게 해줘야 하는 경우가 생길 수 있는데, 이때 필요한 것이 arguments
객체이다.
arguments
의 length
는 위에서 설명한 함수의 length와는 조금 다르다.
함수의 length는 함수를 정의할 때 매개 변수의 수를 담고 있었다면, arguments
의 length
는 함수를 호출할 때 넘겨진 전달 인자의 수를 담고 있다.
arguments
의 length
프로퍼티를 활용하면 다음과 같은 동작이 가능하다.
var func = function(){
if(func.arguments.length < 4){
var sum = 0;
for(var i = 0; i < arguments.length; i++){
sum += arguments[i];
}
return sum;
}else{
var multipli = 1;
for(var i = 0; i < arguments.length; i++){
multipli *= arguments[i];
}
return multipli;
}
};
console.log(func(5,10,15)); // 30
console.log(func(1,2,5,10)); // 100
전달 인자의 수가 3개 이하일 경우에는 모든 전달 인자를 더하고, 4개 이상일 경우에는 곱해서 반환하는 것처럼 전달 인자의 수에 따라 다른 동작을 하는 함수를 만들 수 있다.
이렇게 arguments
객체를 이용해 전달 인자의 수에 따라 다른 동작을 하게 만들고, typeof
연산자까지 이용하게 된다면 비록 JavaScript에서는 없는 개념이지만 오버로딩을 비슷하게 구현할 수도 있다.
prototype
과 __proto__
프로퍼티에 대해 알고싶다면 프로토타입과 프로토타입 체이닝 포스팅을 참고하자!
참고자료
MDN Web Docs - arguments.callee
MDN Web Docs - Function.caller
MDN Web Docs - deprecated caller or arguments usage