javascript : call, apply, bind 차이 - this의 사용

자바스크립트에서는 일반적인 방법 외에도, 함수를 어디서 어떻게 호출했느냐와 관계없이 this가 무엇인지 지정할 수 있다.

call

call 메서드는 모든 함수에서 사용할 수 있으며, this를 특정 값으로 지정할 수 있다.

<script type="text/javascript">  
const bruce = {  
     name : 'Bruce'  
};  

const madeline = {  
    name : 'Madeline'  
};  

//이 함수는 어떤 객체에도 연결되지 않았지만 this를 사용함.  
function greet(){  
    return `Hello, I'm ${this.name}`;  
};  

greet(); // 'Hello, I'm undefined' - this는 어디에도 묶이지 않음  
greet.call(bruce) // 'Hello, I'm Bruce' - this는 bruce  
greet.call(madeline) // 'Hello, I'm Madeline' - this는 madeline  
</script>

함수를 호출하면서 call을 사용하고 this로 사용할 객체를 넘기면 해당 함수가 주어진 객체의 메서드인 것처럼 사용할 수 있다. call의 첫 번째 매개변수는 this로 사용할 값이고, 매개변수가 더 있으면 그 매개변수는 호출하는 함수로 전달된다.

<script type="text/javascript"\>  
function update(birthYear, occupation){  
    this.birthYear = birthYear;  
    this.occupation = occupation;  
};  

update.call(bruce, 1949, 'singer'); // bruce 변경  
/*  
bruce는 이제  
{  
name : 'Bruce',  
birthYear : 1949,  
occupation : 'singer'  
}  
로 변경됨  
*/  

update.call(madeline, 1942, 'actress'); // madeline 변경  
/*  
madeline은 이제  
{  
name : 'Madeline',  
birthYear : 1942,  
occupation : 'actress'  
}  
로 변경됨  
*/  
</script>

apply

apply는 함수 매개변수를 처리하는 방법을 제외하면 call과 완전히 같다. call은 일반적인 함수와 마찬가지로 매개변수를 직접 받지만, apply는 매개변수를 배열로 받는다.

<script type="text/javascript">  
update.apply(bruce, [1955, 'actor']);  
/*  
bruce는 이제  
{  
name : 'Bruce',  
birthYear : 1955,  
occupation : 'actor'  
}  
로 변경됨  
*/

update.apply(madeline, [1918, 'writer']);  
/*
madeline은 이제  
{  
name : 'Madeline',  
birthYear : 1918,  
occupation : 'writer'  
}  
로 변경됨  
*/  
</script>

apply는 배열 요소를 함수 매개변수로 사용해야 할 때 유용하다. apply를 설명할 때 흔히 사용하는 예제는 배열의 최소값과 최대값을 구하는 것이다. 자바스크립트의 내장 함수인 Math.min 과 Math.max 는 매개변수를 받아 그중 최소값과 최대값을 각각 반환한다. apply를 사용하면 기존 배열을 이들 함수에 바로 넘길 수 있다.

<script type="text/javascript">  
const arr = [2,3,-5,15,7];  

Math.min.apply(null, arr); // -5  
Math.max.apply(null, arr); // 15  
</script>

this의 값에 null을 쓴 이유는 Math.min과 Math.max가 this와 관계 없이 동작하기 때문이다. 즉, 무엇을 넘기든 관계없다.

ES6의 확산 연산자(...)를 사용해도 apply와 같은 결과를 얻을 수 있다. update 메서드는 this 값이 중요하므로 call을 사용해야 하지만, Math.min과 Math.max는 this 값이 무엇이든 관계없으므로 확산 연산자를 그대로 사용할 수 있다.

<script type="text/javascript">  
const newBruce = [1940, "martial artist"];  

update.call(bruce, ...newBruce); // apply(bruce, newBruce)와 같음  
Math.min(...arr); // -5  
Math.max(...arr); // 15  
</script>

bind

this의 값을 바꿀 수 있는 마지막 함수는 bind이다. bind를 사용하면 함수의 this 값을 영구히 바꿀 수 있다. update 메서드를 이리저리 옮기면서 호출할 때 this 값은 항상 bruce가 되게끔, call이나 apply, 다른 bind와 함께 호출하더라도 this 값이 bruce가 되도록 하려면 bind를 사용한다.

<script type="text/javascript">  
const updateBruce = update.bind(bruce);  

updateBruce(1904, "actor");  
// bruce 는 이제 { name: "Bruce", birthYear: 1904, occupation: "actor" }  

updateBruce.call(madeline, 1274, "king");  
// bruce 는 이제 { name: "Bruce", birthYear: 1274, occupation: "king" };  
// madeline은 변하지 않음  
</script>

bind는 함수의 동작을 영구적으로 바꾸므로 찾기 어려운 버그의 원인이 될 수 있다. bind를 사용한 함수는 call이나 apply, 다른 bind와 함께 사용할 수 없는 거나 마찬가지다. 함수를 여기저기서 call이나 apply로 호출해야 하는데, this 값이 그에 맞춰 바뀌어야 하는 경우를 상상해보라. 이럴 때 bind를 사용하면 문제가 생긴다. bind를 쓰지 말라고 권하는 것은 아니다. bind는 매우 유용하지만, 함수의 this가 어디에 묶이는지 정확히 파악하고 사용해야 한다.

bind에 매개변수를 넘기면 항상 그 매개변수를 받으면서 호출되는 새 함수를 만드는 효과가 있다. 예를 들어 bruce가 태어난 해를 항상 1949로 고정하지만, 직업은 자유롭게 바꿀 수 있는 업데이트 함수를 만들고 싶다면 다음과 같이 하면 된다.

<script type="text/javascript"\>  
const updateBruce1949 = update.bind(bruce, 1949); updateBruce1949("singer, songwriter");  
/*  
bruce 는 이제 {  
name: "Bruce",  
birthYear: 1949,  
occupation: "singer, songwriter"  
}  
*/  
</script>

출처:https://ibrahimovic.tistory.com/29[Web Standard]

+ Recent posts