this ?
자바스크립트에서 this는 무엇을 의미할까? 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수(self-reference variable)를 말한다. 여기서 가리키는 대상이 '또는'으로 바뀌는 이유는, 어디에서 어떻게 호출하느냐에 따라 this의 값이 결정되기 때문이다.
전역 범위
전역 범위(Global context), 자바스크립트에서 일반적으로 this를 호출하면, this는 window라는 전역 객체를 가리킨다. window 객체라는 것은 현재 실행되고 있는 자바스크립트의 모든 변수, 함수, 객체 DOM 등을 포함하고 있는 객체로, 만물의 근원이 되는 객체이다.
함수 범위
함수 내에서 this를 호출할 경우, 현재 실행되고 있는 코드의 문맥(context)에 따라 this가 달라진다.
단순 함수 호출
// 1. 일반 함수 범위에서 호출
function outside() {
console.log(this); //this는 window
// 2. 함수 안에 함수가 선언된 내부 함수 호출
function inside() {
console.log(this); //this는 window
}
inside();
}
outside();
우선 가장 일반적인 방법으로 함수를 선언한 후 호출하는 경우, this는 window 객체이다. 함수 안에서 또 함수를 선언하더라도, this는 여전히 window를 가리킨다.
객체의 메소드(Method)
// 1. 객체 또는 클래스 안에서 메소드를 실행한다면 this는 Object 자기 자신을 가리킨다.
let man = {
name: 'John',
hello: function() {
// 2. 객체이므로 this는 man을 가리킴
console.log('hello' + this.name);
}
}
man.hello(); // 3. hello John
이제부터 this가 바뀌기 시작한다. 첫 번째 경우는, 바로 객체(Object)나 클래스 내부에 선언된 메소드 함수이다. 함수를 어떤 객체의 메소드로 호출하면, this 값은 그 객체를 사용한다.
함수를 객체 외부에서 선언하고, 객체 안에서 호출하는 경우에도 this는 해당 객체의 this를 참조한다.
// 3. 일반 함수 welcome을 선언
function welcome() {
// 4. 여기서 this는 전역 객체 window이므로, 만약 실행시키면 undefined가 출력
console.log('welcome ' + this.name);
}
// 5. man 객체의 welcome 속성에 일반 함수 welcome을 추가
man.welcome = welcome;
// 6. welcome이 man 객체에서 호출되었으모로 welcome John이 출력됨
man.welcome();
이와는 반대로, 객체의 함수를 외부에서 호출할 때, this는 window가 된다.
// 7. man 객체의 thanks 속성에 함수를 선언
man.thanks = function () {
// 8. man.thanks()를 호출하면 thanks John이 출력
console.log('thanks ' + this.name);
}
// 8. 이 함수를 객체 외부에서 참조
let thankYou = man.thanks;
// 9. 객체 외부이므로 this가 window 객체가 되어서 thanks (undefined)가 출력
thankYou();
// 10. 또 다른 객체에서 이 함수를 호출하면?
women = {
name: 'Barbie',
thanks: man.thanks; // 11. man.thanks 함수를 women.thanks에 참조
}
// 12. this가 포함된 함수가 호출된 객체가 women이므로 thanks Barbie가 출력
women.thanks();
여기서 조심해야할 점!
바로 메소드에서 내부 함수를 선언하면 this가 어떻게 될까?
let man = {
name: 'John',
// 1. 이것은 객체 메소드
hello: function() {
// 2. 객체 메소드 안에서 함수를 선언하는 것이므로 내부 함수
function getName() {
// 3. 여기서 this가 가리키는 것은?
return this.name;
}
console.log('hello ' + getName()); // 4. 내부 함수를 출력시키고
}
}
man.hello(); // 메소드를 실행시키면 undefined가 뜬다. this는 window를 가리킴.
객체의 메소드에서 this가 객체를 가리키고 있던 것과는 다르게, 내부 함수에서 this는 window 객체를 가리키고 있다. 내부 함수는 엄밀히 말해 메소드가 아니기 때문에, 단순 함수 호출 규칙에 따라 window를 가리키고 있다는 점에서 유의해야 한다.
call(), apply(), bind()
그럼 위 코드를 의도대로 만들려면 어떻게 해야 할까?
자바스크립트에서는 각각 다른 문맥의 this를 필요에 따라 변경할 수 있도록 함수를 제공한다. call(), apply(), bind() 등이 있는데, 여기서는 call()을 사용해 예시를 들어보겠다.
// 1. 객체의 메소드
let man = {
name: 'John',
// 2. 객체의 메소드 안에서 함수를 선언하므로 내부 함수
hello: function() {
function getNmae(){
// 3. 여기서 this는?
return this.name;
}
// 4. 이번에는 call()을 통해 현재 문맥에서의 this(man객체)를 바인딩 해주었다.
console.log('hello ' + getName.call(this));
}
}
// 이번에는 메소드를 실행시키면 John이 뜬다. this가 man 객체로 바인딩 된 것을 확인할 수 있다.
man.hello();
콜백 함수
// 1. 콜백 함수
let object = {
callback: function() {
setTimeout(function() {
console.log(this); // 2. this는 window
}, 1000);
}
}
콜백 함수에서는 this가 window를 가리킨다. 객체 안에 메소드로 선언되어 있어도.
생성자
함수 앞에 new 키워드를 붙이고 선언할 때, this를 해당 객체에 바인딩한다.
// 1. 클래스 역할을 할 함수 선언
function Man() {
this.name = 'John';
}
// 2. 생성자로 객체 선언
let john = new Man();
// 3. this가 Man 객체를 가리키고 있어 이름이 정상적으로 출력된다.
john.name; // 'John'
ECMAScript6 문법인 class를 이용해 작성할 수도 있다.
// 1. Class Man 선언
class Man {
constructor(name){
this.name = name;
}
hello(){
console.log('hello ' + this.name);
}
}
// 2. 생성자 실행
let john = new Man('John');
john.hello(); // 3. hello John 출력
여기서 주의할 점은 new 키워드를 붙이지 않을 경우 this가 해당 객체로 바인딩 되지 않기 때문에 window 객체를 건드리는 일이 발생할 수 있다. 따라서 new 키워드를 꼭 써주도록 하자.
화살표 함수
화살표 함수는 ECMAScript6 에서 새로 추가된, 함수를 축약해서 사용할 수 있는 문법이다. 단순히 함수를 축약해서 사용하는 것 뿐만 아니라 this를 외부 스코프에서 정적으로 바인딩된 문맥(정적 컨텍스트, Lexical context)을 가진다는 특징을 갖고 있다.
여기서 정적 컨텍스트는, 소스코드가 작성된 그 문맥의 실행 컨텍스트나 호출 컨텍스트에 의해 결정된다고 한다. 즉, 정적 컨텍스트는 함수가 실행된 위치가 아니라, 정의(defined)된 위치에서의 컨텍스트를 참조한다는 의미이다. 이 코드가 어디서 실행되는 것이 중요한 것이 아니라, 그냥 정의된 부분에서 가까운 외부 함수의 this만 보면 되는 것이다.
// 1. 화살표 함수
let obj = {
a: this, // 2. 일반적인 경우 this는 window
b: function() {
console.log(this); // 3. 메소드의 경우 this는 객체
},
c: () => {
console.log(this);
// 4. 화살표 함수의 경우 정적 컨텍스트를 지님, 함수를 호출하는 것과 this는 연관 없음
// 5. 즉, 화살표 함수가 정의된 obj 객체의 this를 바인딩하므로, this는 window
}
}
obj.b() // 6. obj
obj.c() // 7. window
일반적인 방법으로 함수를 선언(function() { ... })하면, 일반적인 함수는 함수가 실행될 때 자체적으로 this를 할당하게 되는데, 이 함수는 메소드 함수이므로 this가 메소드를 포함하는 객체로 바인딩된다.
하지만 화살표 함수는 this가 없기 때문에, 부모 스코프의 this를 바인딩하는데, 위의 예시에서 이는 곧 window 객체를 의미한다. 따라서 메소드로 화살표 함수를 쓰면 this를 이용한 부모 객체에 접근할 수 없다.
참고
'JavaScript' 카테고리의 다른 글
주요 JavaScript 이론 정리 (0) | 2024.03.11 |
---|---|
자바스크립트 엔진의 동작 원리 (0) | 2023.10.14 |
[JavaScript] 얕은 복사(Shallow copy)와 깊은 복사(Deep copy) (0) | 2023.09.20 |
[JavaScript] == 와 === 의 차이 (0) | 2023.09.19 |
DFS(Depth-First Search) : 재귀함수와 스택 프레임 (0) | 2023.08.05 |