이 포스팅은 https://www.codementor.io/niladrisekhardutta/how-to-call-apply-and-bind-in-javascript-8i1jca6jp, https://www.codementor.io/dariogarciamoya/understanding--this--in-javascript-du1084lyn?icn=post-8i1jca6jp&ici=post-du1084lyn 에 있는 포스팅들을 번역한 것입니다. 오역이나 의역이 있을 수 있습니다. 지적해주시면 확인 후 바로 정정하겠습니다.
original source of this posting is from https://www.codementor.io/niladrisekhardutta/how-to-call-apply-and-bind-in-javascript-8i1jca6jp, https://www.codementor.io/dariogarciamoya/understanding--this--in-javascript-du1084lyn?icn=post-8i1jca6jp&ici=post-du1084lyn If the original author requests deletion, it will be deleted immediately.
Translated by Jake Seo (서진규)
- https://velog.io/@jakeseo_me
- https://github.com/n00nietzsche
자바스크립트 개발자라면 알아야 할 33가지 개념 #15 자바스크립트 : this, call(), apply(), bind()
함수가 만들어졌을 때, 뒤에서는 this
라 불리는 키워드가 만들어집니다. this
는 함수가 동작하는 곳에 있는 오브젝트와 연결해줍니다.
this
키워드의 값은 그 함수 자체와는 상관이 없습니다. 함수가 어떻게 불려지는지가 this
의 값을 결정합니다.
// define a function
var myFunction = function () {
console.log(this);
};
// call it
myFunction();
this
의 값은 어떤 것이 될지 예측할 수 있을까요? 기본 값으로, this
는 언제나 전역 스코프의 root을 참조하는 window Object가 됩니다. 만일, 스크립트가 strict mode("use strict"
) 내에서 작동하고 있다면, this
는 undefined일 것입니다.
var myObject = {
myMethod: function () {
console.log(this);
}
};
여기서 this
의 값으론 무엇이 들어올까요?
정답은 우린 모른다 입니다.
기억해두세요. this 키워드의 값은 언제나 함수 그 자체와는 상관 없습니다. 함수가 어떻게 호출되는지가 this
의 값을 결정합니다.
코드를 조금만 바꿔봅시다.
var myMethod = function () {
console.log(this);
};
var myObject = {
myMethod: myMethod
};
이제 더욱 분명해질 겁니다.
물론, 여전히 this
의 값은 우리가 함수를 어떻게 호출하냐에 따라 달렸습니다.
코드 안의 myObject
는 myMethod
라 불리는 프로퍼티를 갖습니다. 이 프로퍼티는 myMethod
함수를 가리킵니다. myMethod
함수가 글로벌 스코프로부터 호출됐을 때, this
는 window object를 참조합니다. myObject
의 메소드로서 호출됐을 때는, this
가 myObject
를 참조합니다.
myObject.myMethod() // this === myObject
myMethod() // this === window
이러한 형식은 묵시적 바인딩(implicit binding) 이라 불립니다.
함수에 명시적으로 컨텍스트를 바인딩할 때, 그것을 명시적 바인딩이라 합니다. 이러한 동작은 주로 call()
메소드와 apply()
메소드에 의해 이뤄집니다.
var myMethod = function () {
console.log(this);
};
var myObject = {
myMethod: myMethod
};
myMethod() // this === window
myMethod.call(myObject, args1, args2, ...) // this === myObject
myMethod.apply(myObject, [array of args]) // this === myObject
명시적 바인딩과 묵시적 바인딩 중에 어느 쪽이 더 우선순위를 가질까요?
var myMethod = function () {
console.log(this);
};
var obj1 = {
a: 2,
myMethod: myMethod
};
var obj2 = {
a: 3,
myMethod: myMethod
};
obj1.myMethod(); // 2
obj2.myMethod(); // 3
obj1.myMethod.call( obj2 ); // ?????, 직접 해보세요.
obj2.myMethod.call( obj1 ); // ?????
명시적인 바인딩은 묵시적 바인딩보다 우위를 갖게 됩니다.
하드 바인딩은 bind()
(ES5)으로 가능합니다. bind()
메소드는 우리가 지정한 this
컨텍스트를 가진 기존 함수를 불러오기 위해 하드코딩된 새로운 함수를 반환합니다.
myMethod = myMethod.bind(myObject);
myMethod(); // this === myObject
하드바인딩은 명시적 바인딩보다 우위를 갖게 됩니다.
var myMethod = function () {
console.log(this);
};
var obj1 = {
a: 2
};
var obj2 = {
a: 3
};
myMethod = myMethod.bind(obj1); // 2
myMethod.call( obj2 ); // 2 명시적 바인딩은 obj2이나, obj1로 하드바인딩 되어있음
function foo(a) {
this.a = a;
}
var bar = new foo( 2 );
console.log( bar.a ); // 2
새로운 new 인스턴스를 참조하는 함수가 호출되었을 때, this
가 만들어집니다.
역자 주: 위의 경우에는 변수 bar가
this
가 되었습니다.
함수가 new와 함께 호출되었을 때는 묵시적, 명시적 또는 하드 바인딩을 신경쓰지 않습니다. 이 때는 그냥 새로운 인스턴스인 새로운 컨텍스트를 만들어냅니다.
function foo(something) {
this.a = something;
}
var obj1 = {};
var bar = foo.bind( obj1 );
bar( 2 );
console.log( obj1.a ); // 2
var baz = new bar( 3 );
console.log( obj1.a ); // 2
console.log( baz.a ); // 3
역자 주: 위에서 bar 변수를 obj1로 바인딩 하였고, new 키워드 없이는 바인딩된대로 잘 동작하였으나, new 키워드가 붙은 이후에는 새로운 컨텍스트를 만들었습니다.
때때로, 우리는 라이브러리나 헬퍼오브젝트를 사용합니다. (Ajax, event handling, etc.) 그리고 전달된 콜백을 호출합니다. 이러한 경우에는, this의 동작을 주의해야 합니다.
myObject = {
myMethod: function () {
helperObject.doSomethingCool('superCool',
this.onSomethingCoolDone);
},
onSomethingCoolDone: function () {
/// Only god knows what is "this" here
}
};
코드를 보세요. 우리가 "this.onSomethingCoolDone"을 콜백으로 넘겼기 때문에, 스코프가 그 메소드를 참조하고 있다고 생각할 수도 있습니다.
이 부분을 고치기 위해, 몇가지 방법이 있습니다.
myObject = {
myMethod: function () {
helperObject.doSomethingCool('superCool', this.onSomethingCoolDone, this);
},
onSomethingCoolDone: function () {
/// Now everybody know that "this" === myObject
}
};
myObject = {
myMethod: function () {
helperObject.doSomethingCool('superCool', this.onSomethingCoolDone.bind(this));
},
onSomethingCoolDone: function () {
/// Now everybody know that "this" === myObject
}
};
this
를 me
에 캐시할 수도 있습니다.myObject = {
myMethod: function () {
var me = this;
helperObject.doSomethingCool('superCool', function () {
/// Only god knows what is "this" here, but we have access to "me"
});
}
};
이 방법은 추천드리지 않습니다. 왜냐하면 메모리 누수를 초래할 수 있고, 진짜 스코프를 잊게 만들고 변수에 의존하게 만들기 때문입니다. 스코프가 정말 엉망이 되는 지경에 다다를 수도 있습니다.
This 문제는 이벤트 리스너, 타임아웃, forEach와 같은 것들에도 적용됩니다.
이 포스트에서 우리는 간단한 자바스크립트 코드 예제를 통해, call()
, apply()
, bind()
메소드의 차이에 대해 알아볼 것입니다. 자바스크립트에서 함수들은 오브젝트들이고, 위 3개의 메소드는 함수의 호출을 제어하기 위해 사용됩니다. call()
과 apply()
는 ECMAScript 3에서 소개되었고, bind()
메소드는 ECMAScript 5에서 추가되었습니다.
함수를 즉시 호출하기 위해서 call()
/ apply()
메소드를 사용할 수 있습니다. bind()
는 함수가 나중에 실행됐을 때도 원본 함수를 호출할 때 갖는 올바른(correct) 컨텍스트(this)가 bind된 함수를 반환합니다. 그래서 bind()
는 특정 이벤트에서 함수가 나중에 호출될 필요가 있을 때, 유용합니다.
call()
또는 Function.prototype.call()
아래 call()
메소드의 샘플코드를 확인해보세요.
//Demo with javascript .call()
var obj = {name:"Niladri"};
var greeting = function(a,b,c){
return "welcome "+this.name+" to "+a+" "+b+" in "+c;
};
console.log(greeting.call(obj,"Newtown","KOLKATA","WB"));
// returns output as welcome Niladri to Newtown KOLKATA in WB
call()
메소드의 첫번째 파라미터는 함수가 호출되는 순간 "this" 오브젝트 값을 세팅합니다. 이 경우에는 "obj" 가 this 오브젝트였습니다.
나머지 파라미터들은 실제 함수의 인자들이었습니다.
apply()
또는 Function.prototype.apply()
아래 apply()
메소드의 샘플코드를 확인해보세요.
//Demo with javascript .apply()
var obj = {name:"Niladri"};
var greeting = function(a,b,c){
return "welcome "+this.name+" to "+a+" "+b+" in "+c;
};
// array of arguments to the actual function
var args = ["Newtown","KOLKATA","WB"];
console.log("Output using .apply() below ")
console.log(greeting.apply(obj,args));
/* The output will be
Output using .apply() below
welcome Niladri to Newtown KOLKATA in WB */
call()
메소드와 비슷하게 동작합니다. 첫번째 파라미터는 함수가 호출되는 순간 "this" 의 값을 세팅합니다. 위의 경우에는 "obj" 오브젝트였습니다. apply()
메소드가 call()
메소드와 유일하게 다른 점은 두번째 파라미터에서 실제 함수의 인자 값을 배열로 받는다는 것입니다.
bind()
또는 Function.prototype.bind()
아래 bind()
코드의 샘플을 확인해보세요.
//Use .bind() javascript
var obj = {name:"Niladri"};
var greeting = function(a,b,c){
return "welcome "+this.name+" to "+a+" "+b+" in "+c;
};
//creates a bound function that has same body and parameters
var bound = greeting.bind(obj);
console.dir(bound); ///returns a function
console.log("Output using .bind() below ");
console.log(bound("Newtown","KOLKATA","WB")); //call the bound function
/* the output will be
Output using .bind() below
welcome Niladri to Newtown KOLKATA in WB */
bind()
메소드에 대한 위의 코드예제에서 우리는 context를 가진 나중에 호출될 bound 함수를 반환합니다. 우리는 bound 함수가 콘솔에서 다음과 같이 정의된 것을 볼 수 있습니다.
bind() 메소드에 대한 첫번째 파라미터는 역시 bound 함수가 호출될 때, 타겟 함수에서 "this" 의 값을 세팅하는 부분입니다. bound 함수가 "new" 연산자를 이용하여 생성됐을때는, 바인드 시킨 this 값(첫번째 파라미터의 값)이 무시된다는 것을 알아두셔야 합니다. 나머지 파라미터들은 인자로 잘 넘겨집니다.
만일 당신이 초보자라면, 일단은 여기까지 알아두시면 될 것 같습니다. 읽어주셔서 감사합니다.
API 호출에 때때로, 때때로 라고 되어있는데 원문과 비교하니 한 번만 들어가는게 맞는 것 같습니다!
잘 보고 있습니다~