this
는 함수 실행시 호출(invocation) 방법에 의해 결정되는 특별한 객체입니다. 함수 실행시 결정되므로, 실행되는 맥락(execution context)에 따라 this
는 다르게 결정됩니다.
함수가 무엇이냐가 중요한 것이 아니라, 어떻게 실행되는 것인지가 중요합니다. 함수 실행의 다섯가지 방법을 소개합니다.
console.log(this)
foo()
obj.foo()
new Foo()
foo.call()
foo.apply()
이 함수 실행의 다섯가지 방법에 따라 this 역시 다섯가지 바인딩 패턴이 존재합니다. 그 중 아래 세가지 형태는 반드시 기억해야 합니다.
패턴 | 바인딩되는 객체 | 설명 |
---|---|---|
Method 호출 | 부모 객체 (실행 시점에 온점 왼쪽에 있는 객체) | 하나의 객체에 값과 연관된 메소드를 묶어서 사용할 때 주로 사용함 |
new 키워드를 이용한 생성자 호출 | 새롭게 생성된 인스턴스 객체 | 객체 지향 프로그래밍에서 주로 사용함 |
.call 또는 .apply 호출 | 첫번째 인자로 전달된 객체 | this 값을 특정할 때 사용하며, 특히 apply의 경우 배열의 엘리먼트를 풀어서 인자로 넘기고자 할 때 유용함 |
1번 Global 형태의 사용이나, 2번의 Function 호출 방법을 통해 함수를 실행할 경우에는, 애초에 this를 사용하지 않는 것을 권장합니다. Function 호출시 this를 사용할 이유는 없습니다. 흔히 사용하지 않는 방법입니다. 1,2번 방법의 함수 호출시, 다음과 같이 경우에 따라 바인딩이 다양하게 달라집니다. 아래 내용은 굳이 기억할 필요는 없습니다. 애초에 사용하지 않으면 됩니다.
패턴 | 바인딩되는 객체 | 설명 |
---|---|---|
Global | window (strict mode에서는 undefined) | module.exports |
Function 호출 | window (strict mode에서는 undefined) | global |
메소드 호출은 객체.메소드()
과 같이 객체 내에 메소드를 호출하는 방법을 의미합니다.
단순 객체를 사용한 Singleton 패턴에서 이러한 예제를 흔히 볼 수 있습니다. 다음은 카운터를 구현한 예제입니다.
let counter1 = {
value: 0,
increase: function() {
this.value++ // 메소드 호출을 할 경우, this는 counter1을 가리킵니다
},
decrease: function() {
this.value--
},
getValue: function() {
return this.value
}
}
counter1.increase()
counter1.increase()
counter1.increase()
counter1.decrease()
counter1.getValue() // 2
Singleton 패턴은 단 하나의 객체만 만들 수 있으므로, 똑같은 기능을 하는 카운터를 여러 개 만드려면, 아래 예제 코드와 같이 클로저 모듈 패턴을 이용하거나, 클래스로 만들어서 생성자 호출과 같이 사용할 수 있습니다.
function makeCounter() {
return {
value: 0,
increase: function() {
this.value++ // 메소드 호출을 할 경우, this는 makeCounter 함수가 리턴하는 익명의 객체입니다
},
decrease: function() {
this.value--
},
getValue: function() {
return this.value;
}
}
}
let counter1 = makeCounter()
counter1.increase()
counter1.getValue() // 1
let counter2 = makeCounter()
counter2.decrease()
counter2.decrease()
counter2.getValue() // -2
생성자 호출은 객체.메소드()
과 같이 객체 내에 메소드를 호출하는 방법과 비슷하지만, 객체가 new
키워드를 이용해서 만들어졌다는 것이 다릅니다. 이 때의 객체는 우리가 인스턴스라고 부릅니다. 즉 인스턴스.메소드()
의 형태의 호출입니다.
카운터를 클래스로 만들어봅시다.
class Counter {
constructor() {
this.value = 0; // 생성자 호출을 할 경우, this는 new 키워드로 생성한 Counter의 인스턴스입니다
}
increase() {
this.value++
}
decrease() {
this.value--
}
getValue() {
return this.value
}
}
let counter1 = new Counter() // 생성자 호출
counter1.increase()
counter1.getValue() // 1