this, javascript
What is this, categorize all this cases.

Q. What on earth, this
is interpretated in js?
TL;DR
this
는 동적으로 해석된다.- 일반적으로 호출하는 주체가 parameter로 전달된다.
- 즉
obj.method(...)
는method(this=obj, ...)
로 해석
Global context
commonJS
:this
===globalThis
(window
orglobal
)type="module"
:this
는 언제나undefined
Function
: method로 활용되지 않는 일반적인 함수에서this
는 2가지로 해석됩니다.use strict
: undefinednon strict
: globalThis
Arrow functions
:outer scope
의this
를reference
하는 변수를 closure로 보존(lexical scoping
)한다.Callback
: 일반적으로 this가 전달되지 않아 function과 동일하게 처리되지만, 일부 API (JSON.parse(text, reviver)
)들은 내부적으로 this를 넣어준다.Constructor
(new
):new
를 통해 호출되는 constructor는 내부적으로this
에 생성될 instance를 할당한다.super
: 부모의 context가 아닌, super.method()를 호출한 context의 this가 적용된다.Class
- static vs instance
derived class constructor
에서 super()를 호출하지 않거나, return object하지 않는 이상 this는 생성되지 않는다.
EventHandler
- 대부분 브라우저는
addEventListener
의 경우, handler에 현재 element를 this에 bind시켜서 줍니다. - 인라인 이벤트 핸들러에서도 this에 현재 이벤트 리스닝되는 element가 바인드됩니다.
<button onclick="alert(this);">Show this</button>
- 대부분 브라우저는
0. this
intro
-
Javascript에서
this
키워드는, 일반적으로 instance에 bind되는 대부분 언어와 달리, 동적으로 해석되며 호출한 방법에 의해 결정됩니다. -
즉 동적으로 해석되는 this는 아래와 같이 호출되는 주체에 따라서 다르게 해석됩니다.
|
|
- 전형적인 function 호출에서, this는
function's prefix
(dot 앞에 있는 part)를 통해 implicitly하게 parameter로 전달됩니다.
|
|
- 물론 explicitly하게 this를 지정하여 전달할 수도 있습니다.
- Function.prototype.call()
- Function.prototype.apply()
- Reflect.apply()
- 또는 this를 bind시켜서 function을 새롭게 생성할 수도 있습니다.
- Funciton.prototype.bind()
이제 차근차근 this가, context에 따라서 어떻게 다르게 해석되는지 알아보도록 하겠습니다.
1. Global context
strict mode
상관없이 globalThis
property를 의미하며, 이는 실행환경에 따라서 2가지로 해석됩니다.
- node:
global
- browser:
window
|
|
2. Function declaration의 context
use strict
를 사용하는지에 따라서 2가지 경우가 발생합니다.
|
|
3. Callbacks
iterative array methods
,Promise
constructor case
- 일반적으로 callback으로 함수를 넘겨준다면 this는 bind되지 않았기 때문에
- “strict”: undefined
- “non-strict”:
globalThis
|
|
- 가끔 어떤 API들은 this를 넣어주는 경우도 있습니다.
- JSON.parse(text, reviver?)
- JSON.stringify(value, replacer?)
The reviver is called with the object containing the property being processed as this.
- JSON.parse는
reviver
함수에 parse처리된 상태를 this로 넣어서 실행시켜준다.
|
|
4. Arrow functions
Arrow function
은 lexical context의 this를 유지합니다. 다른 말로 일반 function과 달리 호출 방식에 따라서 dynamic하게 this가 변경되지 않습니다.
lexical context
, a.k.astatic context
, 동적으로 this가 처리되는 것이 아닌, 코드 작성 위치에서 this가 지정됨.
-
Arrow function는 closure처럼 구현되어
this
value를 감싸고 있는 scope에서 들고 있다고 생각하면 됩니다. (auto-bound
). -
또한 arrow function은 call(), bind(), apply()를 통해서 this를 동적으로 묶어주더라도 무시합니다.
|
|
object literal은 아래와 같이 this값이 그 자체로 없기 때문에, outer scope의 this인 globalThis를 받습니다.
|
|
- 경험상에 따르면 마치 arrow function이 static하게 this를 지정하고 있는 것으로 이해되고 있지만, 사실 arrow function은 outer scope의 this를 가리키는 reference를 closure로 들고 있는 것입니다.
outer의 this를 가리키는 reference를 closure로 들고 있다는 것이, 무슨 뜻인지는 아래 예시를 보면 더 확실해집니다.
|
|
- 만약 arrow function이 function declaration안에 존재한다면? arrow function의 this는 function declaration의 this를 가리키게 됩니다.
|
|
2번 케이스를 보면 arrow function의 this값이 변경되는 것처럼 보입니다. hi
property는 function declaration을 가지고 있으며, function declaration은 dot
앞의 주체를 this로 하여 param에 전달되는 것처럼 동작합니다.
하지만 const hi는 dot앞의 주체가 없기 때문에 strict mode에서는 this가 undefined로 할당되게 됩니다. arrow function은 외부 scope의 function의 this를 reference하고 있기 때문에, 해당 function의 this가 undefined이기 때문에, 마치 변경된 것 처럼 동적으로 변화되어 undefined을 return합니다. 그러므로 arrow function의 this 또한 동적으로 호출 방법에 따라서 변경되는 것처럼 동작가능합니다.
이런 현상 때문에 위에서 arrow function의 this는 lexical scope의 outer scope의 this를 closure의 this로 reference하고 있다고 표현한 것입니다. arrow function의 this인 reference는 여전히 변경되지 않았기 때문입니다.
5. Constructors (new operator
)
- function이
new
를 통해 constructor로 사용되면, js는 내부적으로 constructor 함수 안의 this를 생성되는 instance로 할당합니다
|
|
즉 원래 function declaration이 global context에서 사용되면, this가 window 또는 undefined로 해석되지만, new
operator를 사용하게 될 경우 내부적으로 this를 instance로 할당해서 처리하게 됩니다.
6. super
- 자녀에서 super의 method를 호출했을 때, super의 method안에 this는, super의 값과 상관없이
super.method()
를 감싸고 있는 context의 this로 처리됩니다.
|
|
즉 위와 같은 경우 Parent의 메서드안에 this는 Parent가 아니라, child의 this를 따릅니다. 왜냐하면 super.getName을 감싸고 있는 child.getName의 this는 child를 가리키고 있기 때문입니다.
7. Class
- static context:
this
=Class
static method
static field (initializer / block)
- instance context:
this
=instance
constructor
method
instance field
|
|
Derived class(child) constructor(extends
)
파생 클래스 생성자
: 파생 클래스(자식 클래스) 생성자는 기본 클래스(부모 클래스) 생성자와 달리 초기에 this 바인딩이 없습니다.
|
|
-
super()를 호출하면 생성자 내에 this 바인딩이 생성되고, 이것은 사실상
this = new Base();
라는 코드를 실행하는 것과 같은 효과를 가집니다. 여기서 Base는 기본 클래스를 의미합니다. -
주의 사항:
super()
를 호출하기 전에this
를 참조하려고 하면 오류가 발생합니다(당연히 this가 없으니), 그러므로 생성자안에서 this를 사용한다면, 그 보다 더 위에 super()가 존재해야 합니다.
|
|
super()
호출 규칙: 파생 클래스의 constructor는super()
를 호출하지 않고 반환해서는 안 됩니다. 단, 생성자가 객체를 반환하여 this 값을 덮어쓰는 경우나 클래스에 생성자가 전혀 없는 경우는 예외입니다.
|
|
JS
의 child class에서 constructor를 명시적으로 작성하지 않으면, 내부적으로 constructor를 생성하고, 이 생성자에서는 super()를 자동으로 호출합니다.
|
|
8. DOM Event Handler
8.1. 함수 이벤트 핸들러
- 대부분의 브라우저에서, 이벤트 핸들러로 사용되는 함수의
this
는 리스너가 부착된 DOM 요소에 바인딩 시킵니다.
|
|
8.2. 인라인 이벤트 핸들러
- 인라인에서 사용되는 this는 이벤트 리스너가 부착된 element 입니다.
|
|
- 하지만, 내부 scope를 추가로 가지게 된다면, global context로 해석됩니다.
|
|
즉 이는 다른말로, function을 정의해서 인라인에 집어넣더라도 동일하게 global context로 해석된다는 뜻입니다.
|
|
지금까지 내용들을 정리하면 아래와 같은 테스트 코드를 작성해볼 수 있습니다.
|
|