사실 arguments는 그리 자주 쓰는 객체는 아니다. 하지만 화살표 함수( arraow function )를 배우는 사람이라면 화살표 함수( () => {} ) arguments가 없다는 설명을 들은 적은 있을 것이다.
오늘은 arguments가 어떤 속성을 가지고 있는지, 또 어떤 상황에서 사용 할 수 있을지 확인 해보도록 하자. 예제 코드를 위주로 다룰 터이니 한 번 살펴보고 적절한 상황에서 사용해보자.
arguments?
function foo(a, b) {
console.log(a, b) // 1 2
console.log(arguments) // Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
foo(1, 2)
function foo(a, b) {
console.log(a, b) // 1 2
console.log(arguments) // Arguments(2) [1, 2, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
foo(1, 2)
일반 함수(function)에서 사용 할 수 있는 객체로 함수 호출시의 인자들을 확인 할 수 있다.
=> a, b는 매개변수(parameter) foo(1, 2) 인자(argument)라고 보면 된다.
함수 호출 시의 값을 알 수 있기에,
function foo(a, b) {
console.log(a, b) // 1 2
console.log(arguments) // Arguments(2) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
}
foo(1, 2, 3)
위와 같이 정의 되지 않은 매개변수도 받을 수 있다.
arguments.length
arguments는 길이를 가지고 있다. 인자의 갯수를 알려준다. 하지만 배열은 아니다. 정확히는 키와 값을 가지고 있는 Map에 더 가까운 구조를 가지고 있다. 아래의 JSON.stringify만 보더라도 확인이 가능하다.
function foo(a, b) {
console.log(arguments.length) // 3
console.log(arguments[0]) // 1
console.log(JSON.stringify(arguments)) // '{"0":1,"1":2,"2":3}'
}
foo(1, 2, 3)
따라서 바로 배열이 가지고 있는 함수는 사용 할 수 없다.
function foo(a, b) {
console.log(arguments.map(v => v * 2)) // Uncaught TypeError: arguments.map is not a function
}
foo(1, 2, 3)
arguments[@@iterator]
이때 사용 할 수 있는 것이 이 속성이다. arguments객체는 iteratro protocol을 충족 하고 있기에,
function foo(a, b) {
for( const v of arguments ) console.log(v) // 1 2 3
const arr = [...arguments]
return arr.map(v => v * 2) // [2, 4, 6]
}
foo(1, 2, 3)
위와 같이 배열로 변환도 쉽고 for ... of 문법도 가능 하다.
arguments.callee
일단 사용 하지 않도록 하자. 하지만 왜 있는 지에 대한 설명은 좀 해주는 것이 맞을테니, 한번 보자. 좀 먼 과거로 돌아가 보자. 솔직히 자바스크립트를 처음 접한 것이 2000년대 초반이라서 내가 어렸을 적 읽었던 책에서도 알 수 없던 내용이라. 나에게도 꽤 흥미로웠다.
자바스크립트의 함수는 익명함수와 유명함수를 지원한다는 것을 알고 있을 것이다.
function foo() {} // 유명 함수
const bar = function() {} // 익명함수를 가지고 있는 bar
유명 함수는 언제부터 지원 된 것일까? 이건 1999년이다. 자 아래의 코드를 한번 보자. 최대한 1998년에 맞추어 코드를 작성 할 테니 한번 확인 해보도록 하자.
getFacArr = function (arr) {
__fac = function (n) { return !(n > 1) ? 1 : __fac(n - 1) * n; }
__r = [];
for( __i = 0; __i < arr.length; __i++ ) __r.push(__fac(arr[__i]));
return __r;
}
console.log(getFacArr([1, 2, 5])); // [1, 2, 120]
console.log(__fac);
참고로 표준 자체가 없던 세대라서, 아마 minified와 같이 후 처리는 아마 못하였을 것 같다. 그래서 변수명도 10자 이내로 사용하였을 것이다.
하지만 var키워드드도 없을 당시라서 내부 변수나 내부 함수등은 __를 붙이는 등 규칙은 정하여서 사용 하였을 것 같다.
var키워드가 없으니, window(global)에 선언한 모든 값이 존재 할 것이고, 외부에서 사용해도 되는 변수와 아닌 변수는 규칙으로 관리하였을 것이다.
자 그러면 callee를 이용 하여 재 구성 해보자.
getFacArr = function (arr) {
__r = [];
for( __i = 0; __i < arr.length; __i++ ) {
__r.push( ( function (n) {
return !(n > 1) ? 1 : arguments.callee(n - 1) * n;
} )( arr[__i] ) )
}
return __r;
}
console.log(getFacArr([1, 2, 5])); // [1, 2, 120]
이제 앞으로 내부 함수가 외부에 노출 될 일이 없어졌다. 뭐 물론 코드 자체는 더욱더 읽기 힘들어졌지만, 글로벌 객체를 더럽힐 일이 없어졌기에 메모리 측면이나 변수를 짓지 않아도 되는 것에 대해서 만족 하였을 지도 모르겠다.
arguments 활용.
사실 나의 경우에는 arguments사용 하는 것을 좋아하지는 않는다. 두개의 값 중 무엇이 큰지 알려주는 max 함수를 만든다고 생각해보자.
function max(a, b) {
return a > b ? a : b
}
아주 간단한 함수이다. 하지만 누군가 여러 개의 값 중에 무엇이 큰지 알고 싶다고 해보자. 만약에 max라는 함수를 많이 사용 하고 있어서 배열로 바꾸기에는 어려운 상황이다.
function max(a, b, c, d, e) {
const arr = [a, b, c, d, e]
let max = a
for( let i = 1; i < arr.length; i++ ) {
max = max < arr[i] ? arr[i] : max
}
return max
}
max(1, 2)
max(1, 2, 3)
max(1, 2, 3, 4)
max(1, 2, 3, 4, 5)
그러면 파라미터를 늘려서 만들어야 할 것이다. 하지만 arguments를 사용 하여 아래와 같이 표현 하면 된다.
function max(a, b) {
const arr = [...arguments]
let max = a
for( let i = 1; i < arr.length; i++ ) {
max = max < arr[i] ? arr[i] : max
}
return max
}
max(1, 2, 3, 4, 5, 8, 34, 2, 3, 5) // 34
파라미터 갯수를 늘리지 않아도 위와 같이 표현이 가능 하다. 하지만, 이렇게 되면 기능이 숨어 버리게 된다. max라는 함수는 겉으로 보기에는 두개의 값중에 큰값을 얻어내는 함수이지만, 여러개의 값을 계산 가능한 것이 된다.
따라서, 두개의 값만 계산 하겠지, 이런 기대를 갖고 호출시 잘못 된 결과를 갖게 된다.
max(5, 4, Infinity)
// 호출 시 예상한 값: 5
// 실제 값: Infinity
자바스크립트는 더욱더 좋은 대한을 제시 하고 있다.
function max(...arr) {
let max = arr[0]
for( let i = 1; i < arr.length; i++ ) {
max = max < arr[i] ? arr[i] : max
}
return max
}
max(1, 2, 34, 2, 3, 5) // 34
기존 방식도 지원 하며, 요즘 IDE를 사용 하고 있으면 해당 함수의 파라미터 정도는 미리 보여줄 테니 호출하는 입장에 헷갈리지 않고 사용 할 것이다.
뭐 물론 저 코드의 단점은 autoCurry를 사용 할 수 없는게 단점이게 된다. autoCurry를 사용 하고 있는 프로젝트라면, 차라리 배열을 받을 수 있는 새로운 함수를 만드는게 나을 것이다.
댓글
댓글 쓰기