this [re:]
this 는 무엇일까?
this
this는 일반적으로 객체지향 언어에서 자기자신을 가리키는 객체, 즉 자기 자신과 굉장히 관련이 깊다.
자바스크립트도 비슷할까?
그렇지 않다. 자바스크립트가 조금 특별하기 때문인데,
자바스크립트의 함수는 객체, 그 중에서도 일급 객체이다.
일급 객체인 자바스크립트 함수는 변수나 데이터에 할당할 수 있고,
다른 함수에 인수 또는 반환값으로도 사용할 수 있다.
// 1. 변수나 데이터에 저장
const myFunc = func
// 2. 함수의 인수로 전달
function func1(func2) {}
// 3. 함수의 반환값으로 사용
function func1() {
...
return func1
}
이런 특징으로 자바스크립트의 함수는 다양한 환경에서 호출될 수 있다.
func(), test.func(), tt.func()
이래서 자기 자신이라는 표현을 사용하는 것이 까다롭다.
이는 함수가 선언된 이후에 어떤 환경에서 어떤 객체에 의해 호출될지 예측할 수 없기 때문이다.
그만큼 this 도 단순하지 않다.
this 는 어떤 객체에 의해 호출되냐에 따라서 this 의 의미가 많이 달라진다.
자바스크립트에서 모든 함수는 this 를 가지고 있다. 그리고 함수가 호출되면 상황에 따라 this 가 가리키는 객체가 달라지고 결정된다.
이렇게 this 가 동적으로 결정되는 것을 this 가 "그 객체에 바인딩 된다" 라고 표현을 한다.
자바스크립트 엔진은 프로그램을 실행하면 모든 실행 가능한 코드를 평가해서 실행 문맥을 만든다.
이때 실행 가능한 코드는 전역 코드, 함수 코드, eval 코드 이다. 이 코드별로 실행 문맥이 만들어 진다.
실행 문맥은 렉시컬 환경 컴포넌트, 디스 바인딩 컴포넌트 등 실제로 실행에 필요한 컴포넌트들로 이루어져 있는데
this 바인딩 컴포넌트에 우리가 알고 있는 this에 대한 정보가 담기게 된다.
프로그램이 실행이 되면 자바스크립트 엔진은 전역 코드를 평가해서 전역 실행 문맥을 만듭니다.
함수가 실행이 되면 전역 코드 실행을 잠깐 멈추고 또 다시 실행 문맥을 만들게 된다. 이 타이밍에 this 바인딩 컴포넌트의 값이 결정된다.
일반적으로 this 는 점앞에 있는 객체 즉 호출 당시 함수를 포함하고 있는 객체에 바인딩된다.
그럼 함수가 객체에 의해 호출되지 않는 상황에서는 어떻게 this 가 바인딩 될까?
단독으로 호출되는 함수나 생성자 함수에서는 어떻게 this 를 이해하면 좋을까?
this binding rules
this 는 기본적으로 4가지 규칙에 의해서 바인딩 된다.
기본 바인딩, 암시적 바인딩, new 바인딩, 명시적 바인딩
이 규칙들은 우선순위가 존재한다.
1. 기본 바인딩
function showThis() {
console.log(this);
}
function showThisStrictMode() {
'use strict'
console.log(this);
}
showThis(); // window
showThisStrictMode(); // undefined
이처럼 함수를 단독호출 했을때 this 는 기본적으로 전역 객체에 바인딩 된다.
브라우저 실행 환경에서는 윈도우 객체에 바인딩 되고, 엄격 모드에서는 전역 객체가 바인딩 대상에서 제외된다.
feat. 노드 환경에서는 global 을 가리킨다. 그리고 {}
2. 암시적 바인딩
const obj = {
name: 'muyeon',
getName() {
return this.name;
}
}
console.log(obj.getName()); // muyeon
. 앞에 있는 객체에 바인딩 된다.
이런 방식을 암시적 바인딩이라고 하는데 조심해야 할 부분이 있다.
const obj = {
name: 'muyeon',
getName() {
return this.name;
},
};
function showReturnValue(callback) {
console.log(callback());
}
showReturnValue(obj.getName); // undefined
예상과 다르게 undefined 가 출력된다.
자바스크립트에서 객체를 할당한 변수는 해당 객체의 참조값을 저장한다.
obj.getName 프로퍼티에는 getName 함수에 대한 참조가 들어 있는 것이다.
참조값을 함수의 인수로 넘기게 되면 함수는 이 참조값을 복제해서 사용을 하게 된다.
이제 같은 객체를 참조하는 또 다른 변수를 만들어서 함수 안에서 사용하게 되는 것이다.
마찬가지로 showReturnValue 에 obj.getName 을 전달하면 동일한 함수를 참조하는 또 다른 레퍼런스가 콜백 변수에 저장되어서 사용된다.
이렇게 되는 이유는 . 연산 때문이다.
. 연산이나 대괄호 연산을 통해서 객체의 프로퍼티에 접근하면 참조 타입이라고 하는 특별한 값을 반환해준다.
참조타입은 자바스크립트 명세서에서만 사용되는 타입인데, 프로퍼티 뿐만 아니라 프로퍼티를 가지고 있는 객체와 strict 모드인지 아닌지에 대한 여부를 같이 가지고 있는 하나의 타입이다.
따라서 . 연산을 통해 얻은 값은 바로 함수를 호출하지 않고서는 암시적 바인딩을 기대할 수 없다.
마찬가지로 인수로 전달된 콜백의 참조도 객체에 대한 어떠한 정보도 포함을 하지 않기 때문에 함수로 단독 호출한 것과 같이 동작을 하게 되는 것이다.
이렇게 한두다리만 건너도 바인딩된 this 를 너무 쉽게 잃어버리는 문제가 있는데 어떻게 이를 해결할 수 있을까?
3. 명시적 바인딩 (call, apply, bind)
this 를 특정 객체에 암시적으로 바인딩할 뿐만 아니라 명시적으로도 바인딩 할 수 있는 방법을 제공한다.
이를 통해 this 가 소실되는 문제를 해결할 수 있다.
함수 객체는 call, apply, bind 를 통해 명시적으로 바인딩을 해결하는 방법을 제공한다.
call(context, arg1, arg2, ...)
apply(context, args)
call 과 apply 를 사용하면 this 를 바인딩할 객체를 지정한 상태로 함수를 호출할 수 있다.
첫번째 인자로 바인딩할 객체를 전달하고 다음에 인수를 넣어준다.
call 과 apply 는 인수를 전달하는 방법에만 차이가 존재한다.
func.bind(context, arg1, arg2, ...)
bind 메서드는 this 가 참조하는 객체를 고정시켜 준다.
이 메서드가 반환하는 특수한 객체가 있는데 그 객체는 마치 항상 this가 어떤 특정 객체에 바인딩되어 있는 것처럼 그런 함수처럼 행동한다.
이렇게 항상 같은 객체에 바인딩 되도록 강제하는 방법을 하드 바인딩이라고도 부른다.
4. new 바인딩
자바스크립트의 함수를 new 방식으로도 호출할 수 있다.
이렇게 호출하게 되면 생성자 함수로서의 역할을 수행할 수 있게 된다.
{
obj = {} // create new Object
this = obj // bind
this.name = "muyeon" // obj : { name: "muyeon" }
return this
}
new 연산자를 사용해 this 가 바인딩되는 규칙을 new 바인딩이라고 한다.
5. 바인딩 우선 순위
new 바인딩 > 암시적 바인딩 > 명시적 바인딩 > 기본 바인딩
arrow function
화살표 함수는 다른 함수들이랑 this 를 바인딩하는 방법이 다르다.
const obj = {
name: 'muyeon'
showNameInSec(sec) {
setTimeout(() => {
console.log(this.name);
}, sec);
}
}
obj.showNameInSec(1000); // muyeon
화살표 함수는 상위 실행 문맥을 유지한다.
렉시컬 스코프와 관계 없이 호출 당시에는 의존하는 기존의 바인딩 규칙은 화살표 함수 안에서 this 에게 의미가 없다.
화살표 함수 안에서 this 는 선언될 당시에 상위 스코프 그 스코프에 해당하는 실행 문맥 상의 this 바인딩 컴포넌트를 참조한다.
쉽게 말해 상위 스코프의 this 를 가리킨다.
이런 this 를 렉시컬 this, 어휘적 this 라고 부른다.
참고링크
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this
https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-this-%EC%B4%9D%EC%A0%95%EB%A6%AC
https://www.youtube.com/watch?v=7RiMu2DQrb4
https://im-developer.tistory.com/96