JavaScript arguments 객체

자바스크립트의 함수는 파라미터를 함수 내부의 객체 arguments에 저장합니다.
배열의 특징을 가지고 있지만 배열의 메서드가 없기 때문에 유사배열이라고도 부릅니다.

1. arguments

1) 정의

arguments는 함수의 지역변수로, 함수의 파라미터를 배열 형태로 가지는 변수입니다.

또한, arguments는 유사배열이라고도 불립니다. length, 인덱스 접근등 배열의 특징을 가지고 있지만, map, forEach 등 배열의 함수를 사용할 수 없기 때문입니다.

2) 예시

function sumAll() {
	let result = 0

	// 파라미터를 지정해주지 않아도, arguments 객체에 받을 수 있습니다.
	for(const item of arguments) {
		result += item
	}

	console.log('arguments', arguments)
	
	return result
}

console.log('sumAll', sumAll(1, 2, 3, 4, 5)) // 15

sumAll 함수는 파라미터를 지정해주지 않았지만 arguments를 사용해서 파리미터들의 합을 계산할 수 있습니다.

3) 유사배열

MDN에 따르면 유사배열의 의미는 다음과 같습니다.

참고: 'Array 형태'란 arguments가 length 속성과 더불어 0부터 인덱스 된 다른 속성을 가지고 있지만, Array의 forEach, map과 같은 내장 메서드를 가지고 있지 않다는 뜻 입니다.

즉, 배열처럼 length, index 등의 속성은 있지만, map, forEach등 배열의 메서드는 사용할 수 없습니다.

또한, Array.isArray로 검사를 해보면 결과는 false 입니다.

function temp() {
    console.log('isArray', Array.isArray(arguments))
}

temp(1, 2, 3) // isArray false

4) 활용

arguments 정해지지 않은 수의 파라미터를 받을 때 유용해보이기도 합니다.

다만, 파라미터를 지정해주지 않았기 때문에 함수를 보고 이러한 목적으로 작성되었다는 느낌을 받기 어렵습니다.

여러모로 아쉬운 느낌이 든다면 바로 Rest 파라미터에 대해 알아봅시다.

2. Rest 파라미터

1) 정의

MDN에 따르면 Rest 파라미터의 의미는 다음과 같습니다.

Rest 파라미터 구문은 정해지지 않은 수의 파리미터를 배열로 나타낼 수 있게 합니다.

2) 예시

// 스프레드 연산자를 사용해서 파라미터를 받습니다.
function sumAll(... args) {
    console.log('args', args) // args [1, 2, 3, 4, 5]
    
	console.log('isArray', Array.isArray(args)) // isArray true

	return args.reduce((previous, current) => {
		return previous + current
	})
}

console.log('sumAll', sumAll(1, 2, 3, 4, 5)) // 15

sumAll 함수는 파라미터로 받은 값들의 합을 리턴하는 함수입니다.

Rest 파라미터 args는 배열이기 때문에, 반복문 대신 배열 메서드인 reduce를 사용해서 전체합을 구하는 코드를 직관적으로 작성할 수 있습니다.

3) arguments 와 비교

arguments와 비교해보았을 때 Rest 파라미터의 장점은

    1. 함수를 봤을 때 정해지지 않은 개수의 파라미터를 받는 함수임을 바로 알 수 있습니다.
    1. 배열메서드를 사용할 수 있기 때문에 직관적인 코드작성이 가능합니다.

... 그렇다면 정말, arguments는 필요없을까요...

3. arguments 활용

function max(a, b) {
	
	console.log('함수에서 지정한 파라미터 개수', max.length)
	console.log('실제로 받은 파라미터 개수', arguments.length)

	if(max.length !== arguments.length) {
		console.warn('max 함수는 2개의 파라미터를 받아 2개중 최대값을 찾는 함수입니다. 2개 이상의 파라미터를 받아 앞의 2개의 파라미터로만 계산되었습니다.')
	}

	return a + b
}

console.log(max(1, 2, 3)) // 3

1) function.length

자바스크립트 함수의 length 속성에는 정의한 파라미터의 개수가 저장되어 있습니다.

function max(a, b) {
	return a > b ? a : b
}

console.log('max.length', max.length) // 2 - a, b 2개의 파라미터가 있습니다.

2) arguments.length

만약 다음 2개가 다르다면, 기대되는 함수의 쓰임과 다르게 받았다는 것을 경고로 보여줄 수 있습니다.

function.length - 함수에 지정된 파라미터 개수
argumens.length - 실제로 받은 파라미터의 개수

3) 파이어폭스 Gecko 예시

다음은 파이어폭스 Gecko엔진의 코드의 일부분으로, Array의 map 메서드의 소스코드입니다.

/* ES5 15.4.4.18. */
function ArrayForEach(callbackfn/*, thisArg*/) {
    /* Step 1. */
    var O = ToObject(this);

    /* Steps 2-3. */
    var len = ToLength(O.length);

    /* Step 4. */
    if (arguments.length === 0)
        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.prototype.forEach");
    if (!IsCallable(callbackfn))
        ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));

    /* Step 5. */
    var T = arguments.length > 1 ? arguments[1] : void 0;

    /* Steps 6-7. */
    /* Steps a (implicit), and d. */
    for (var k = 0; k < len; k++) {
        /* Step b */
        if (k in O) {
            /* Step c. */
            callContentFunction(callbackfn, T, O[k], k, O);
        }
    }

    /* Step 8. */
    return void 0;
}

Array의 map 메서드는 배열의 원소에 각각에 대해, 파라미터로 받은 콜백함수를 적용한 결과를 리턴합니다.

map 메서드는 1개의 함수를 파라미터로 받아야하며 그 이외의 상황에 대해서는 예외처리를 진행합니다.

if (arguments.length === 0)
	ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.prototype.forEach");
var T = arguments.length > 1 ? arguments[1] : void 0;

4. function.call

1) arguments를 배열로 만들기

MDN 의 자료를 보면 다음의 방법을 사용해서 유사배열인 arguments를 배열로 만듭니다.

function f() {
	var normalArray = Array.prototype.slice.call(arguments)
}

여기서 조금 더 살펴본다면..

2) call 이란?

사실, 자바스크립트에서 함수를 호출하는 방법은 한개 이상 입니다.

대부분은 함수명()을 사용합니다만, function.call() 또한 함수를 호출할 수 있습니다.

call의 차이점은 call의 첫번째 인자값은 호출된 함수의 내부에서 this로 사용된다는 것입니다.

var normalArray = Array.prototype.slice.call(arguments)

위의 예시에서 Array 객체의 내부의 slice 메서드를 사용하면서 첫번째 인자값으로 arguments를 건네주었고, slice 함수의 this는 arguments로 바뀌어 동작하게됩니다.

3) 확인

Array.isArray 를 사용해서 확인해보면, true라고 출력이되고,

map, reduce 등 배열의 메서드를 활용하는 것이 가능합니다.

function sumAll() {
    // arguments 객체
    console.log('arguments', arguments)

    // Array 객체의 this를 arguments로 지정 후, slice 메서드 사용
    const arr = Array.prototype.slice.call(arguments) 
    
    // 배열 arr 출력
    console.log('arr', arr)
    
    // 배열의 메서드 map사용, [2, 4, 6]
    console.log(arr.map((chip) => {return chip * 2}))
    
    // 배열 여부 확인, true
    console.log('isArray', Array.isArray(arr))

   // Array 객체의 redcue 메서드를 사용해서 배열의 전체합 리턴
   return Array.prototype.reduce.call(arguments, (prev, cur) => {
    	return prev + cur
   })
}

console.log(sumAll(1, 2, 3)) // 6

4) call, apply, bind

여담으로 call 과 같이 함께 다니는 것들에는 call, apply, bind가 있습니다. 다음 포스트에서 알아봅시다.

좋은 웹페이지 즐겨찾기