arguments
객체가 배열과 비슷해 보일수 있으나 배열처럼 동작하지 않는다. 예를 들어 shift()
라는 함수가 있는데, 이 메서드는 배열의 첫번째 요소를 젝하고 뒤이은 모든 요소들을 하나씩 이동시킨다.
var fruits = ["apple", "banana", "orange"];
var firstFruit = fruits.shift();
console.log(firstFruit); // 출력: "apple"
console.log(fruits); // 출력: ["banana", "orange"]
arguments
에서는 이 shift()
를 사용할 수 없다. Array타입이 아니기 때문이다.
그렇다면 call
메서드를 사용해서 shift
함수를 추출해 arguments
객체에 호출할 수 있지 않을까 ? 다음 예제를 살펴보자
function callMethod(obj, method){
var shift = [].shift; // 함수를 변수에 저장
// arguments 객체에 shift 사용
shift.call(arguments); //obj 제거
shift.call(arguments); //method 제거
return obj[method].apply(obj, arguments);
}
하지만 이 함수는 제대로 동작하지 않는다.
var obj = {
add : function(x,y) { return x+y;}
};
callMethod(obj, "add", 17,25);
//오류 정의되지 않은 apply 프로퍼티 읽을 수 없음
이 함수에tj 오류가 발생하는 이유는 arguments
객체가 함수의 arguments
의 복사물이 아니기 때문이다. 이게 무슨 말이냐면 obj
랑 method
를 지운다면 obj
에 17
이 들어가고 method
에 25
가 들어가게 되어 결과적으로 obj[method]
가 아닌 17[25]
가 나오게 된다. 그리고 두개를 다 지워버린 arguments
에는 여전히 27
15
가 들어있게 된다. 때문에 apply
프로퍼티를 추출하려는 시도는 실패하게 된다.
즉 arguments
객체를 수정하면 지정된 파라미터 값이 달라진다는 말이다.
strict 모드를 사용하면 더욱 불안정해진다.
function strict(x){
"use strict"
arguments[0] = 'modified'
return x == arguments[0];
}
function nonStrict(x){
arguments[0] = 'modified'
return x == arguments[0];
}
strict("unmodified") //false
nonstrict("unmodified") //true
스트릭트 모드
에서 함수 파라미터는 자신의 arguments
객체를 별명으로 삼지 않는다.
때문에 arguments
객체를 수정하는건 안하는게 좋다. 정 하고 싶다면 배열의 복사본을 만들어서 사용하면 된다.
관례적으로 다음과 같은 코드를 사용한다.
var args = [].slice.call(arguments)
이전 코드를 관례를 사용하여 변경해보자
// slice를 사용한 배열 복사
function callMethod(obj, method){
var args = [].slice.call(arguments,2); //일정 인자 뒤로 전부 가변인자로 사용한다.
return obj[method].apply(obj, args); //가변인자가 배열로 들어오기 때문에 가변인자로 받을 수 있게 apply로 변경함
}
apply
로 받아왔다.var obj = {
add : function(x,y) { return x+y;}
};
callMethod(obj, "add", 17,25); // 42 출력
이제 잘 동작한다.
arguments
객체를 절대 수정하지 마라arguments
객체를 수정하기 전에 [].slice.call(arguments)
를 호출해 진짜 배열로 복사하라