자바스크립트에서는 일반적인 방법 외에도 함수를 어디서 어떻게 호출했느냐에 관계 없이 this가 무엇인지 지정할 수 있다.
그럼 여기서 this는 정확히 뭔데? 라고 의문이 들 것이다.
this
는 자바스크립트에서 다른 언어와 비교했을 때 조금 다르게 동작한다. 대부분의 경우, this
의 값은 함수를 호출하는 방법에 의해 결정이 되어진다. 자바스크립트에서는 선언하면서 결정되는 것이 있고, 호출할 때 결정되는 게 있다. 선언할 때 결정되는 것은 클로저가 있다. 선언된 위치에 따라 달라진다. this
의 핵심은 호출하는 방법에 의해서 결정된다!!
const someone = {
name: 'yeji',
whoAmI: function(){
console.log(this);
}
};
someone.whoAmI(); // {name: "yeji", whoAmI: f}
const myWhoAmI = someone.whoAmI;
myWhoAmI(); // Window {postMessage: f, blur: f, ...}
// someone.whoAmI()와 myWhoAmI()는 서로 같은 function을 의미하지만 식을 출력해보면 값이 달라지는 것을 알 수 있다. 호출하는 방법이 달라졌기 때문이다. this를 알기 위해서는 호출을 무엇이 했는지 알아야한다. 즉, 직접 호출하는 그 코드를 살펴보아야 한다.
첫번째 호출하는 someone.whoAmI();
의 호출하는 직접적인 부분은 someone
인 것이다. myWhoAmI();
는 자바스크립트 코드를 실행하는 자체 글로벌에 있고, 글로벌은 window이기 때문에 window가 호출하는 셈인 것이다.
즉, 자바스크립트의 this는 this === 호출한 객체
이다!!!
call()
메소드는 모든 함수에서 사용할 수 있으며, this를 특정값으로 지정할 수 있다.
const mike = {
name: "Mike",
}
const tom = {
name: "Tom",
}
function showThisName(){
console.log(this.name);
}
showThisName(); // 아무런 값이 뜨지 않음
showThisName().call(mike); // "Mike"
mike와 tom 객체가 있고, showThisName()이라는 함수가 있다. 이 함수는 this.name을 보여준다. showThisName();
을 호출하면 아무런 값이 뜨지 않는다. 왜냐하면 여기서 this는 window를 가르키기 때문에 window.name은 "" 빈 문자열 이어서 아무런 값이 뜨지 않는다.
이 때 showThisName().call();
을 해주고 mike를 전달해준다면 Mike
가 나오게 된다. 함수를 호출하면서 call을 사용하고 this로 사용할 객체를 넘기면 해당 함수가 주어진 객체의 메소드 인 것처럼 사용할 수 있다.
const mike = {
name: "Mike",
}
const tom = {
name: "Tom",
}
function showThisName(){
console.log(this.name);
}
function update(birthYear, occupation){
this.birthYear = birthYear;
this.occupation = occupation;
}
update.call(mike, 1999, "singer");
console.log(mike);
// {name: "mike", birthYear: 1999, occupation: "singer"}
update.call(tom, 2002, "teacher");
console.log(tom);
// {name: "tom", birthYear: 2002, occupation: "teacher"}
call
의 첫번째 매개변수는 this로 사용할 값이고, 매개변수가 더 있으면 그 매개변수라는 함수로 전달되어진다.
const mike = {
name: "Mike",
}
const tom = {
name: "Tom",
}
function showThisName(){
console.log(this.name);
}
function update(birthYear, occupation){
this.birthYear = birthYear;
this.occupation = occupation;
}
update.apply(mike, [1999, "singer"]);
console.log(mike);
// {name: "mike", birthYear: 1999, occupation: "singer"}
update.apply(tom, [2002, "teacher"]);
console.log(tom);
// {name: "tom", birthYear: 2002, occupation: "teacher"}
apply
는 함수 매개변수를 처리하는 방법을 제외하면 call과 완전히 같다. call은 일반적인 함수와 마찬가지로 매개변수를 직접 받지만, apply는 매개변수를 배열로 받는다. []
배열로 묶어주기만 하면call()
과 동일한 결과값을 반환한다.
const minNum = Math.min(3, 10, 1, 6, 4);
const maxNum = Math.max(3, 10, 1, 6, 4);
console.log(minNum); // 1
console.log(maxNum); // 10
const nums = [3, 10, 1, 6, 4];
console.log(Math.min(nums)); // NaN
console.log(Math.max(nums)); // NaN
const minNum = Math.min.apply(null, nums);
const maxNum = Math.max.apply(null, nums);
console.log(minNum) // 1
console.log(maxNum) // 10
apply
는 배열요소를 함수 매개변수로 사용할 때 유용하다. 최댓값과 최솟값을 구하는 코드가 있다고 하면 배열로 넣었을 시에 NaN이 나오게 된다.
apply()
는 두 번째 매개변수로 배열을 전달하게 되면 그 요소들을 차례대로 인수로 사용한다. 그러니깐 (null, nums)
사용하면 결과값이 동일하다.
const mike = {
name: "Mike",
}
function update(birthYear, occupation){
this.birthYear = birthYear;
this.occupation = occupation;
}
const updateMike = update.bind(mike);
updateMike(1980, 'police');
console.log(mike);
// {name: "Mike", birthYear: "1980", occupation: "police"}
어떻게 호출되었는지 개의치 않고 설정할 수 있는 것이 bind
메소드 이다. 즉, this를 고정시킨다는 의미이다. update함수를 이리저리 옮기면서 호출하면 this값을 "Mike"로 나오게 하려면 bind를 사용하며 된다. const updateMike = update.bind(mike);
이 bind는 새로 바인딩할 함수를 만든다. 이 함수는 항상 mike를 this로 받는다.
const user = {
name: "Mike",
showName function(){
console.log("hello", ${this.name});
},
}
user.showName(); // hello, Mike
let fn = user.showName;
fn(); // hello, -> 아무값도 나오지 않음
fn.call(user); // hello, Mike
fn.apply(user); // hello, Mike
const boundFn = fn.bind(user);
boundFn(); // hello, Mike
fn();
을 호출하면 아무값도 나오지 않는다. fn();
을 할당할 때 this를 잃어버리게 되는 것이다. 메소드는 .
앞에 있는 게 this이다. 호출할 때 fn()
만 호출하니깐 this가 없는 것이다. 이럴 때에는 call을 사용하면 fn.call(user);
this로 사용할 값 user를 넣어주면 된다.
정리
this는 호출하는 방법에 의해서 결정되어진다. 이는 굉장히 헷갈릴 수 있기 때문에 이와 관계없이 this를 지정하기 위해서는 call, apply, bind를 사용할 수 있다!
참조)