페이지

2012년 1월 3일 화요일

Function.prototype.apply() 메소드란?

ECMAScript 5.1 Edition의 설명에 따르면 다음과 같다.


15.3.4.3 Function.prototype.apply (thisArg, argArray)

When the apply method is called on an object func with arguments thisArg and argArray, the following steps are taken:
1. If IsCallable(func) is false, then throw a TypeError exception.
2. If argArray is null or undefined, then
    a. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and an empty list of arguments.
3. If Type(argArray) is not Object, then throw a TypeError exception.
4. Let len be the result of calling the [[Get]] internal method of argArray with argument "length".
5. Let n be ToUint32(len).
6. Let argList be an empty List.
7. Let index be 0.
8. Repeat while index < n
    a. Let indexName be ToString(index).
    b. Let nextArg be the result of calling the [[Get]] internal method of argArray with indexName as the argument.
    c. Append nextArg as the last element of argList.
    d. Set index to index + 1.
9. Return the result of calling the [[Call]] internal method of func, providing thisArg as the this value and argList as the list of arguments.
The length property of the apply method is 2.

NOTE     The thisArg value is passed without modification as the this value. This is a change from Edition 3, where a undefined or null thisArg is replaced with the global object and ToObject is applied to all other values and that result is passed as the this value.





위 설명을 하나씩 따라가보자

func라는 객체가 있다고 가정하고, IsCallable(func)가 true가 되려면 func 객체가 [[Call]]이라는 내부 메소드를 가지고 있어야 한다. 즉, 함수 콜 표현식으로 호출될 수 있어야 한다는 뜻이다. IsCallable(func)가 false라면 더 이상 단계가 진행되지 않고 TypeError를 발생시킨다.


var TestObj = {
x: 1,
y: 2
};


// TypeError가 발생한다.
TestObj.apply(this, [3, 4]);    


apply()는 thisArg와 argArray 두 개의 인자를 받게 되는데, argArray가 null이거나 undefined이면 this 값과 비어있는 인자 리스트를 제공하는 func 내부 메소드 [[Call]]의 호출 결과를 리턴한다.


function test_func(a, b) {
document.writeln(arguments.length + "<br>");
this.result = a + b;
return this;
}


var TestObj = {
x: 1,
y: 2
};


// arguments.length는 0이다.
var result = test_func.apply(TestObj, null);   
// TestObj를 리턴하였기 때문에 1,2가 출력된다
document.writeln(result.x + ", " + result.y + "<br>");  


argArray의 타입이 Object가 아니면 TypeError가 발생한다.


function test_func(a, b) {
this.result = a + b;
return this;
}


var TestObj = {
x: 1,
y: 2
};


var a = 1;


// TypeError가 발생한다.
var result = test_func.apply(TestObj, a);   



len에는 arrayArray["length"]가 대입된다.
n에는 len을 부호없는 32비트 정수로 변환하여 대입된다.
argList는 비어있는 리스트가 된다.
index에는 0이 대입된다.
index < n 일 동안 다음을 반복한다.
    a. indexName에는 index를 문자열로 변환하여 대입한다.
    b. nextArg에는 argArray[indexName]을 대입한다.
    c. argList의 마지막 요소로 nextArg를 추가한다.
    d. index에 1을 더하여 index에 대입한다.
인자 리스트인 argListthis 값인 thisArg를 제공하는 func의 내부 메소드 [[Call]]을 호출한 결과를 리턴한다.



function test_func(a, b) {
this.result = a + b;
return this;
}


var TestObj = {
x: 1,
y: 2
};
var tmp_array = [2, 3];
test_func.apply(TestObj, tmp_array);
document.writeln(TestObj.result);  // 5


TestObj에는 원래 없었던 result 속성이 추가되었다.


정리하면,

func.apply(thisArg, argArray)에서 argArray는 func()의 인자로 사용되고, 그 결과는 thisArg에 추가된다.

댓글 없음: