this
?
this
는 Object 내에서 해당 객체를 참조하는 키워드로, 보통 클래스를 만들 때 메소드를 호출한 객체를 참조하거나 이벤트 리스너를 호출한 DOM을 지정할 때 많이 사용한다. 전역에서 동작할 때는 전역 객체(window
, global
)을 참조하며, 함수 내부에서는 함수를 호출하는 방법에 따라 다르게 참조한다.
JavaScript에서 this
바인딩
Javascript의 this
는 다른 언어와 달리 상황에 따라 다르게 바인딩 된다. 앞서 말했듯 전역 공간에서는 전역 객체를 참조하고, 메서드 호출 시 메서드 내부에 있다면 해당 메서드를 호출한 객체를 참조한다. 하지만 함수 안에 있다면 어떨까?
function f1() {
return this;
}
// 브라우저
f1() // window
// Node.js
f1() // global
함수를 호출 할 때 함수 내부의 this
는 해당 함수를 참조하는게 아니라 지정이 되지 않고, 지정되지 않은 this
는 자동으로 전역 객체를 바라보기 때문에 위와 같은 결과가 나오게 된다.
const cat = {
name: 'meow',
foo1: function() {
const foo2 = function() {
console.log(this.name);
}
foo2();
}
};
cat.foo1(); // undefined
일반적인 방법으로 함수를 호출하면 나오게 되는 상황이다. cat.foo1()
메소드 호출 시 내부 함수 foo2
가 실행되는데, 메서드 내부의 함수가 호출이 되었으므로 foo2
내부의 this는 지정되지 않아서 전역 객체를 참조하게 된다. 전역 객체(window
, global
)에 name
이라는 프로퍼티는 없기 때문에 undefined
를 출력하게 된다.
화살표 함수() =>
와 this
의 상관관계
원래 함수를 선언할 땐 function
이라는 키워드를 쓰지만 ES6 도입 이후 화살표 표시(=>
)를 사용해 함수를 선언하는 화살표 함수가 도입이 되었다. 두 방법은 모두 동일하게 작동하지만 화살표 함수는 다음과 같은 제약사항이 있다.
- 무조건 익명함수로만 사용 가능 (대신 변수에 담아서 호출 가능)
- 메서드나 생성자 함수(
constructor
)로 사용할 수 없음
이러한 제약사항이 있음에도 화살표 함수를 사용하는 이유는 단 하나, this
바인딩에서 차이가 있기 때문이다. 화살표 함수에는 this
가 아예 존재하지 않고, 선언될 시점에서의 상위 스코프가 this
로 바인딩된다.
JavaScript에서는 어떤 식별자(변수)를 찾을 때 현재 환경에서 그 변수가 없으면 바로 상위 환경을 검색합니다. 그렇게 점점 상위 환경으로 타고 타고 올라가다가 변수를 찾거나 가장 상위 환경에 도달하면 그만두게 되는 것이죠. 화살표 함수에서의this
바인딩 방식도 이와 유사합니다. 화살표 함수에는this
라는 변수 자체가 존재하지 않기 때문에 그 상위 환경에서의this
를 참조하게 됩니다.
일반적인 함수선언을 다시 떠올려보자. 일반적인 함수는 this
가 존재하지만 지정되지 않았고 지정되지 않은 this
는 전역 객체(window
, global
)를 참조하기 때문에 아까 보았던 예시같은 상황이 나오는 것이다.
const cat = {
name: 'meow',
foo1: function() {
const foo2 = () => {
console.log(this.name); // 상위 스코프 foo1을 호출하는 cat을 참조
}
foo2();
}
};
cat.foo1(); // meow
위의 코드를 다시 보면 아까와 같지만 foo2
가 화살표 함수로 쓰였다. 이 때 cat.foo1
메서드를 호출하게 되면 화살표 함수 내의 this는 상위 스코프로 타고 올라가 메서드를 호출한 cat
객체를 참조하여 의도한 대로 동작하게 만들어 준다.
화살표 함수는 만능이 아니다
JavaScript의 this
함수 바인딩 문제를 화살표 함수가 해결해 주지만, 사용해선 안되는 때가 많다. 상위 환경의 this
를 참조하는 것이 문제가 될 수 있기 때문이다.
1. 메서드
const cat = {
name: 'meow';
callName: () => console.log(this.name);
}
cat.callName(); // undefined
이 경우, callName
의 메서드 내부 this
는 기존 바인딩(메서드로 호출된다면 호출한 메서드를 참조한다는 정의)을 무시하고 자신을 호출한 객체 cat
이 아닌 함수 선언 시점의 상위 스코프인 전역 객체를 참조하게 된다. 일반 함수를 사용해도 메서드로 호출하면 자신을 호출한 객체를 가리키기 때문에 메서드에서는 화살표 함수를 쓸 필요가 없다.
2. 생성자 함수
const Foo = () => {};
const foo = new Foo() // TypeError: Foo is not a constructor
생성자 함수로는 사용할 수 없도록 만들어졌기 때문에 TypeError가 발생된다.
3. addEventListener()
의 콜백 함수
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log(this); // Window
this.innerHTML = 'clicked';
});
button.addEventListener('click', function() {
console.log(this); // button 엘리먼트
this.innerHTML = 'clicked';
});
원래 addEventListener
의 콜백함수에서 this
는 해당 이벤트 리스너가 호출된 엘리먼트가 바인딩되도록 따로 정의가 되어 있기 때문에, 화살표 함수를 쓰게 되면 기존 바인딩을 무시하고 상위 스코프를 참조하게 되어 의도한대로 동작이 안될 수 있다. 단, 상위 스코프의 속성을 쓰기 위해 의도한 경우 사용해도 무방하다.
Uploaded by N2T