this는 참으로 애매한 녀석이었다. 뭔가 친해질만한가 싶으면 멀어져 있었고, 잘 사용하는 것 같다가도 매번 사용할 때마다 새로운 녀석이었다. this에 대해서 자세히 알아보기 위해 여러 블로그를 읽어봤지만 여러 글을 읽은 결과, this는 어떻게 바인딩 되는가? 를 중점적으로 생각해봤다. this가 어떻게 바인딩 되는지 알면 여러 다른 상황에서 그 this의 값을 예측하고 이를 제대로 사용할 수 있을 것이라고 생각했기 때문이다.
일단, 바인딩이라고 하는 것은 쉽게 생각해서 값을 연결해주는 것이라고 생각하면 된다. this는 기본적으로 함수가 호출되는 방식에 따라 그 값이 달라지는데 이것을 바인딩이라고 생각하면 된다.
this의 바인딩은 기본적으로 4가지와 화살표 함수에 따라 달라지는 1가지가 있다.
1. 기본 바인딩
when? 다른 바인딩이 적용되지 않을 때 적용되는 것이 기본 바인딩이다.
기본 바인딩으로 적용되는 this는 전역 객체를 가리킨다.
window.a = 800;
function x() {
console.log(this.a);
}
x(); // 800
단, 'use strict'를 사용하는 엄격 모드에서는 기본 바인딩에서 전역 객체인 window는 제외 된다. 엄격 모드에서 this를 참조하면 그 결과 값은 undefined가 될 것이다.
2. 암시적 바인딩
when? 함수가 객체의 메서드로 호출될 때 적용되는 바인딩을 암시적 바인딩이라고 한다.
this는 해당 함수를 호출한 그 객체에 바인딩 된다.
const 객체 = {
a: 800,
x: function () {
console.log(this.a);
}
}
객체.x(); // 800
하지만 암시적 바인딩에 문제가 하나 있다.
when? 객체의 메서드로 함수를 호출하는데, 이 함수를 다른 함수의 매개변수 즉, 콜백함수로 전달하면 기본 바인딩이 적용이 되어 this는 window를 가리킨다. 엄격모드에서도 그러하다. 이를 암시적 소실이라고 부른다.
why?
const test = {
a: 800,
x: function () {
console.log(this.a);
}
}
setTimeout(test.x, 1000);
위의 코드에서 setTimeout안의 콜백 함수로 사용할 test.x는 test라는 변수에 객체의 x함수를 의미하는 것이 아니라, 그냥 함수x를 의미하게 된다. 객체 안의 x함수를 지칭 하는 것이 아닌, 곧바로 x함수를 지칭하는 것이므로 기본 바인딩이 적용되어 window를 가리킨다.(암시적 소실)
이를 보완하기 위해, 명시적 바인딩이 나왔다.
3. 명시적 바인딩
자바스크립트는 기본적으로 함수가 call, apply, bind라는 메서드를 갖는다. 이를 호출하여 this 바인딩을 명시하는 것을 명시적 바인딩이라고 한다. 즉, 3가지 메서드를 사용하여 직접 객체를 명시(지정)할 수 있는 것이다.
1) call과 apply
how?
const test1 = {
a: 800
}
const test2 = {
a: 700
}
function x() {
console.log(this.a)
}
x.call(test1); // 800
x.apply(test2); // 700
✔️ 여기서 잠깐 알고가는 call과 apply의 차이!
우리가 함수를 호출하는 방법으로 (); 를 쓰지만, call과 apply는 함수를 호출하면서 this를 바인딩한다. 여기서 call과 apply 모두 위에 있는 것처럼 첫번째 인자는 this가 된다. 다만 두번째 인자부터는 조금 다르다.
해당 함수의 인자로 쓰이는 parameter는 this를 명시하는 첫번째 인자 뒤에서부터 다르다.
2) bind
how?
const test1 = {
a: 800
}
function x() {
console.log(this.a)
}
const b = x.bind(test1);
b(); // 800
bind 메서드의 경우, 매개변수로 전달된 오브젝트를 참조해서 this가 해당 오브젝트를 가리키고 그 바인딩된 함수를 다시 반환한다.(하드 바인딩)
하드 바인딩된 함수는 이 다음부터 호출될 때마다 처음 정한 this바인딩을 가지고 호출한다.
4. new 바인딩
new키워드는 생성자 함수를 위해 사용된다.(코드를 작성하는 약속인 컨벤션으로 생성자 함수의 이름은 대문자로 시작한다.)
그리고 이때 this는 생성자 함수를 사용하여 생성할 객체를 가리킨다.
function Xfunc() {
this.a = 700;
}
const v = new Xfunc();
console.log(v.a); // 700
5. arrow function
화살표 함수에서의 this는 앞의 4가지 규칙을 무시한다. 그래서 화살표 함수에서 this는 화살표 함수를 선언할 때, lexical scope가 적용되어 선언하는 시점의 객체가 this에 바인딩된다.
lexical scope는 함수를 호출하는 시점이 아닌, 선언하는 시점에 따라 상위 스코프를 정한다는 의미이다.
how?
const x = {
a: 800,
y: function () {
setTimeout(() => {
console.log(this.a);
}, 1000);
}
}
x.y(); // 800
setTimeout의 콜백 함수로 화살표 함수가 선언 되었는데, 이때 this는 lexical scope가 적용 되어 const x를 가리킨다.
그렇기 때문에 콜백함수로 사용할 때 아주 좋다!