자바스크립트에서 this
는 다른 언어에서와는 다르게 동작한다. Java와 같은 언어의 경우, 인스턴스가 생성되면 this
는 항상 해당 객체를 가르키지만, 자바스크립트의 this
는 주로 함수의 호출 시점에 결정되기 때문에, "이것" 이라는 이름 값을 하지 못하고 그 대상이 자꾸만 바뀐다.
전역 스코프에서의 this
먼저 전역 스코프에서 this
는 전역 객체(globalThis)을 가르킨다. 브라우저는 Window
, 노드에서는 Global
객체가 될 것이다.
// 브라우저
console.log(this) // Window
함수에서의 this
단순 함수 호출
단순 호출에서도 마찬가지로 this
는 전역 객체를 가르킨다.
function nonStrict() {
console.log(this);
}
nonStrict(); // Window
전역 스코프에서의 this
와 한 가지 다른 점은, 전역 스코프에서는 엄격 모드의 여부와 상관 없이 항상 전역 객체를 가르키지만, 함수 호출에선 엄격 모드가 적용 됐을 때 그 값이 undefined
가 된다.
function strict() {
"use strict";
console.log(this);
}
strict(); // undefined
메소드에서의 this
메소드에서는 this
는 호출 주체(객체)를 가르킨다.
const person = {
name: 'seukjjang',
printThis() {
console.log(this);
}
};
person.printThis(); //{name: 'seukjjang', printThis: ƒ}
맨 처음 강조 했듯, this
는 호출 시점에 결정된다는 점에 주의해야 한다. 위 예시에서 printThis()
는 person
의 메소드로서 호출 되었기 때문에 this
는 호출 주체인 person
을 가르킨다.
하지만 다음과 같이 메소드를 다른 변수에 담아 단독으로 호출하면 어떻게 될까?
const { printThis } = person;
printThis(); //Window
메서드가 아니게 된 `printThis()`의 this
는 다시 전역 객체를 가르킨다.
반대로 다음과 같이 객체의 메소드를 나중에 부여하면 this
는 새로운 객체를 가르킨다.
const person2 = {
name: 'jaesoekjjang'
};
person2.printThis = printThis;
person2.printThis() //{name: 'jaesoekjjang', printThis: ƒ}
이제 함수의 "호출 시점"에 결정된다는 말이 이해될 것이다.
그럼 다음 코드는 어떤 결과를 출력할까?
const person3 = {
name: 'jaesoek',
say() {
function printName() {
console.log(this.name);
}
printName();
}
}
person3.say();
this
가 함수 호출 시점에 결정된다는 것을 생각하면 쉽게 undefined
가 될 것임을 알 수 있다. say()
는 person3
의 메소드로 호출되고 있지만 실질적으로 this
를 갖고 있는 함수인 printName()
은 단순 함수로 호출 되었기 때문이다.
화살표 함수에서의 this
화살표 함수에서 this
는 또 다르게 동작한다. 화살표 함수에서의 this는 함수의 호출 시점이 아닌 선언 시점에 결정된다. 화살표 함수는 자신만의 this
를 갖지 않는데, 그 대신 클로저와 같이 자신의 상위 스코프에서 this
를 검색해 나간다.
앞선 문제를 화살표 함수를 사용해 바꾸면
const person3 = {
name: 'jaesoek',
say() {
const printName = () => {
console.log(this.name);
}
printName();
}
};
person3.say() //jaesoek
이번에는 "jaesoek"이라는 결과를 출력한다. printName()
의 선언 시점에 this
는 자신의 상위 스코프인 say()
와 같은 this
를 참조하게 됐고, say()
의 호출 시점에 this
는 person3
였으니 결과적으로 printName()의
this
도 person3
를 가르키게 된 것이다.
this를 원하는 값으로 바꿀 수 있는 bind 메서드
`bind` 메소드(`Function`의 메소드이다)를 사용하면 함수 호출 시점에 상관 없이 원하는 객체로 this
를 바인딩 할 수 있다. bind()
는 첫 번째 인자로 this
로 설정할 객체를 받고 결과로 바인딩 된 새로운 함수를 호출한다.
function printName() {
console.log(this.name)
}
printName() // undefined
printName.bind(person)() //seukjjang
printName()
의 this
는 Window
객체이고, Window
객체는 name
속성을 갖지 않기 때문에 아무것도 출력되지 않는다. 반면 bind()
가 반환한 함수의 this
는 person
객체로 바인딩 되었기 때문에 객체의 이름이 출력되는 것을 확인할 수 있다. bind()
메소드를 화살표 함수에서 호출하는 경우 의도한 대로 동작하지 않을 것이다. 화살표 함수는 자신의 this
가 없기 때문이다.
그럼 만약 화살표 함수의 부모 함수를 바인딩 하면 어떻게 될까? 이전 문제를 다시 가져와서 확인해보자.
const person3 = {
name: 'jaesoek',
say() {
const printName = () => {
console.log(this.name);
}
printName();
}
};
const { say } = person3;
say() // undefined
앞서 봤듯 say()
는 단순 함수로 호출했기 때문에 Window
객체를 가르키고, printName()
도 같은 this
를 참조해 undefined
가 나온다.
그런데 여기서 bind()
로 다시 person3
를 바인딩 해주면?
say.bind(person3)(); // jaesoek
"jaesoek"을 출력하는 것을 확인할 수 있다. 화살표 함수의 this
는 상위 스코프의 this
를 "참조"하고 있기 때문에 부모 함수의 this
가 다른 값으로 바인딩 되면 따라서 바뀌게 된다.
참고자료
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions
'Javascript' 카테고리의 다른 글
[Javascript] 변수 선언과 호이스팅(feat. TDZ) (2) | 2022.12.27 |
---|---|
[Javascript] 리덕스 만들어보기 - (1) (0) | 2022.07.24 |
[Javascript] requestAnimationFrame(애니메이션 최적화하기) (0) | 2022.04.25 |
[Javascript] Closure (0) | 2022.04.14 |
[Javascript] Tagged Templates (styled-components의 이상한 문법) (1) | 2022.02.25 |
댓글