Java에서는 클래스의 인스턴스 본인 참조를 위해 this
를 많이 사용하고 있었고 거의 해당 역할로만 사용했었어서 익숙했다.
Javascript를 사용할 때 리액트를 사용하다보니 클래스를 사용할 일이 없었고 그 외에도 this
를 사용해본적이 없어서 지금까지 리액트로 개발하면서도 개념을 모르고 있었다.
이번 기회에 Javascript에서의 this
에 대해 면밀히 알아보았다.
자바나 C, C++ 같은 경우 this는 하나의 인스턴스에 고정되지만, 자바스크립트 또는 타입스크립트는 런타임 상에서 this 바인딩이 동적으로 결정된다.
this
를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메소드를 참조할 수 있으며
this
가 가리키는 값은 다른 언어와는 달리 실행 컨텍스트에 따라 달라진다.
즉 어떤 환경의 어디 스코프에서 사용하냐에 따라 달라진다.
Global Context
는 환경에 따라 두가지가 있다.
브라우저에서는 전역 객체 window
를 가리키며 노드환경에서는 전역 객체 global
을 가리킨다.
함수 내부에서의
this
값은 함수를 호출하는 방법에 의해 좌우된다.
선언적 함수에서의 단순 호출
엄격모드일 경우 undefined
이며 엄격모드가 아니라면 기본으로 전역 컨텍스트의 객체를 참조한다.
아래의 경우 this
의 값이 호출에 의해 설정되지 않았기 때문에 window
객체를 참조한다.
function a() {console.log(this);};
a(); // window
함수 내에서 this
는 함수가 호출될 때 결정되는데 선언적 함수는 이미 선언부가 호이스팅을 통해 메모리에 할당되기 때문에 this
도 이 시점에 결정되기 때문에 동적 바인딩이 불가능하다.
call, apply, bind 메소드 사용
해당 메소드들을 사용해 선언적 함수에서의 this를 동적으로 결정할 수 있다.
call: 주어진
this
값 및 전달된 args와 함께 함수를 호출한다.
func.call(thisArg[, arg1[, arg2[, ...]]])
thisArg
인자로 해당 함수 호출에 제공될 this
를 결정한다.
function a() {
console.log(this);
}
a(); // window
a.call("hell"); // 'hell'
a.apply("he"); // 'he'
a.bind("wa")(); // 'wa';
화살표 함수에서의 호출
화살표 함수에서
this
는 자신을 감싼 정적 범위로 전역 코드에서는 전역 객체를 가리킨다.
var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true
화살표 함수는 call
, bind
, apply
를 호출해 this
를 동적으로 지정하려고 해도 무시된다.
let a = () => console.log(this);
a(); // window
a.call('he'); // window
화살표 함수의 this
는 실행 컨텍스트로 동적으로 결정되는 것이 아니라 정의되는 시점에 정적으로 결정되며 항상 함수를 둘러싼 가장 가까운 일반 함수
의 this
를 상속하기 때문에 동적 지정이 안된다.
화살표 함수 자체에는
this
가 없고 본인과 가장 가까운 상위의this
를 사용하는 것이라고 이해했다.
var obj = {
bar: function() {
var x = function() {return this};
return x;
}
};
obj.bar()(); // window
var obj2 = {
bar: function() {
var x = () => this;
return x;
}
};
obj2.bar()(); // {bar: ƒ}
객체의 메소드
함수를 어떤 객체의 메소드로 호출하면 this의 값은 그 객체를 사용한다.
const f = {
a() {
console.log(this);
},
b: function () {
console.log(this);
},
c: () => console.log(this)
};
f.a(); // {a: ƒ, b: ƒ, c: ƒ}
f.b(); // {a: ƒ, b: ƒ, c: ƒ}
f.c(); // window
정의 방법이나 위치에 영향을 받지 않으며 호출하는 시점에
결국 어떤 객체로부터 호출되었는지가 중요하다.
let o = { val: 1 };
function f() {
return this.val;
}
f(); // undefined;
o.f = f;
o.f(); // 1;
o.ff = { f: f, val: 2};
o.ff.f(); // 2;
생성자
함수를 new 키워드와 함께 생성자로 사용하면 this는 새로 생긴 객체에 묶인다.
function My() {
this.my = 123;
}
new My().my; // 123;
function My2() {
console.log(this);
}
new My2();
new
키워드로 인스턴스화 되어 사용되는 함수의 this
는 본인을 가리킨다.
DOM 이벤트 처리
함수를 이벤트 처리기로 사용하면 this는 이벤트를 처리해야되는 요소로 설정된다.
<body>
<button id='btn'>hello</button>
<button id='btn2'>hello2</button>
<script>
document.getElementById('btn').addEventListener('click', function() { console.log(this) });
document.getElementById('btn2').addEventListener('click', () => console.log(this) );
</script>
</body>
addEventListener의 콜백에 이미 호출되는 요소가 this
로 바인딩되도록 정의되어있어
화살표함수를 사용하면 기존 바인딩이 사라지고 상위 스코프로 바인딩된다.
음 자바스크립트에서의 this
는 정말 헷갈리는 것 같다.
사용을 안하다보니 더욱 와닿지 않는 것 같고 정상적으로 100% 이해하지 못했지만 공부하며 얻은 결론은 다음과 같다.
선언적 함수의 this는 동적이여서 예측이 힘들다. 화살표함수를 사용해 외부 스코프로부터의 this
참조를 보장해 예측가능해진다.
객체의 메소드로써 객체를 참조해 무언가를 동작시켜야되거나 이벤트리스너에서 엘리먼트를 참조해야 할 때 정도는 화살표 함수 사용에 주의해야할 것 같다는 생각이 들었다.