Arguments - 매개변수의 이해

From. 프론트엔드 개발자를 위한 자바스크립트 프로그래밍

ECMAScript의 함수 매개변수는 다른 언어의 매개변수와 사뭇 다르게 작동합니다. ECMAScript 함수는 매개변수 숫자를 따지지 않으며 데이터 타입도 체크하지 않습니다. 함수에서 매개변수를 두 개 받도록 만들었더라도 반드시 매개변수 두 개를 넘겨야 하는 건 아닙니다. 매개변수를 한 개, 세 개, 또는 아예 넘기지 않더라도 인터프리터는 이를 에러로 간주하지 않습니다. 이렇게 되는 이유는 ECMAScript의 매개변수가 내부적으로 배열로 표현되기 때문입니다. 이 배열은 항상 함수에 전달되지만 함수는 배열에 어떤 값이 들어 있는지 체크하지 않습니다. 빈 배열이 들어와도 상관없고, 필요한 매개변수보다 더 많이 들어와도 괜찮습니다. 함수는 Arguments라는 객체를 하나 갖는데, 이 객체를 통해 매개변수의 값에 접근할 수 있습니다.

Arguments 객체는 각 매개변수를 대괄호 표기법, 즉 첫 번째 매개변수는 arguments[0], 두 번째 매개변수는 arguments[1] 형태로 접근하며 매개변수 개수를 length 프로퍼티를 통해 알 수 있다는 면에서 배열처럼 동작하기는 하지만 Array의 인스턴스는 아닙니다. 이전 예제에서 sayHi() 함수의 첮 번째 매개변수 이름은 name입니다. arguments[0]으로 접근해도 같은 값을 얻습니다. 즉 위 함수는 다음과 같이 매개변수에 명시적인 이름을 정하지 않고 쓸 수도 있습니다.

							// 기존 함수

							function sayHi(name, message) {
								alert("Hello " + name + ", " + message);
							}

							// 명시적 매개변수가 없는 함수

							function sayHi() {
								alert("Hello " + arguments[0] + ", " + arguments[1]);
								alert(arguments.length);
							}
						

고쳐 쓴 함수에는 이름 붙은 매개변수가 없습니다. name과 message 매개변수를 없앴지만 함수는 의도대로 동작합니다. 이 예제는 ECMAScript 함수의 중요한 특징을 묘사합니다. 이름 붙은 매개변수는 편리하긴 하지만 반드시 필요한 건 아닙니다. 다른 언어와 달리 ECMAScript에서는 매개변수에 이름을 붙인다 해서 함수 시그너처를 만들로 나중에 검사하지 않습니다. 다시 말해 이름 붙은 매개변수의 유효성 검사를 하지 않습니다.

arguments 객체의 length 프로퍼티를 통해 함수에 매개변수가 몇 개 전달되었는지 알 수 있습니다. 다음 예제는 함수를 호출할 때 마다 매개변수를 몇 개 넘겼는지 반환합니다.

							function howManyArgs() {
								alert(arguments.length);	
							}

							howManyArgs("string", 45);
							howManyArgs();
							howManyArgs(12);
						

이 예제는 순서대로 2, 0, 1을 표시합니다. 이런 방법을 통해 개발자는 함수가 받는 매개변수 숫자에 제한을 두지 않고 넘겨 받은 매개변수 개수에 맞게 반응할 수 있습니다. 다음 코드를 보십시오.

							function doAdd() {
								if (arguments.length == 1) {
									alert(arguments[0] + 10);
								}else if (argument.length == 2) {
									alert(arguments[0] + arguments[1]);
								}
							}

							doAdd(10);
							doAdd(30, 20);
						

doAdd() 함수는 매개변수가 한 개 일때는 10을 더하고 매개변수가 두 개일 때는 서로 더해서 반환합니다. 따라서 doAdd(10)는 20을 반환하고 doAdd(30, 20)은 50을 반환하니다. 이런 방식은 오버로딩만큼 좋지는 않지만 ECMAScript의 제한을 경감하기에는 충분합니다.

매개변수에서 또 다른 중요한 점은 다음과 같이 arguments 객체를 이름 붙은 매개변수와 함께 쓸 수 있다는 겁니다.

							function doAdd(num1, num2) {
								if (arguments.length == 1) {
									alert(arguments[0] + 10);
								}else if (argument.length == 2) {
									alert(arguments[0] + arguments[1]);
								}
							}
						

다시 고친 doAdd() 함수에서는 이름 붙은 매개변수 두 개를 arguments 객체와 함께 썼습니다. 이름 붙은 매개변수 num1은 arguments[0]과 같은 값이므로 둘을 바꿔서 쓸 수 있습니다. num2와 arguments[1]도 마찬가지입니다.

arguments 객체에서 한 가지 더 재미있는 점은 이 객체의 프로퍼티 값을 이에 대응하는 이름 붙은 매개변수에서 자동으로 반영한다는 점입니다. 예를 들어 다음 코드를 보십시오.

							function doAdd(num1, num2) {
								arguments[1] = 10;
								alert(arguments[0] + num2);
							}
						

고쳐 쓴 doAdd() 함수는 항상 두 번째 매개변수의 값을 10으로 바꿉니다. 이름 붙은 매개변수는 arguments 객체의 프로퍼티를 자동으로 반영하므로 arguments[1]의 값을 바꾸면 num2도 바뀌며 결국 둘 모두 10이 됩니다. 이 둘은 같은 메모리 공간을 쓰는 건 아닙니다. 둘은 각각 다른 메모리 공간을 사용하지만 값은 반영됩니다. 그런데 이는 동기화가 아니라 단방향 반영입니다. 즉 이름 붙은 매개변수의 값을 바꾸더라도 arguments의 해당 프로퍼티는 바뀌지 않습니다. 기억할 것이 하나 더 있습니다. 매개변수를 하나만 넘기면 arguments[1]의 값을 바꾸더라도 이름 붙은 매개변수에는 아무 영향도 없습니다. 이렇게 되는 이유는 arguments 객체의 length 프로퍼티가 함수 정의에서 정의한 매개변수 숫자를 따르지 않고 함수를 호출할 때 넘긴 이름 붙은 매개변수 목록을 따르기 때문입니다.

함수를 정의할 때 함께 정의한 매개변수를 넘기지 않으면 해당 매개변수에는 자동으로 undefined가 할당됩니다. 이는 변수를 정의하기만 하고 초기화하지는 않는 것과 비슷합니다. 예를 들어 doAdd() 함수를 호출할 때 매개변수를 하나만 넘기면 num2에는 undefined가 할당됩니다.

스트릭트 모드는 arguments 객체의 동작 방식을 여러 가지로 바꿨습니다. 먼저 이전 예제에서 보인 할당 방식은 더 이상 동작하지 않습니다. arguments[1]의 값을 10으로 바꾸더라도 num2는 계속 undefined 상태로 남습니다. 둘째, arguments의 값을 덮어쓰려 하면 문법 에러를 냅니다. 코드는 실행되지 않습니다.