객체 지향 언어에서 함수에서 자기가 속해져있는 객체를 가르킨다. JS도 비슷하게 동작할 것이라고 예상하기 쉽지만, JS에서는 자기 자신이라는 단어가 다소 모호하다. 그 이유는 JS의 함수는 일급 객체이기 때문이다.
따라서 this는 실행 환경에 따라 매우 달라진다.
한 함수에서 this가 가르키는 객체를 지정해 주는 것을 this binding이라고 한다. JS엔진은 실행가능한 코드(전역코드, 함수 코드, eval 코드)에서 실행 문맥(렉시컬 환경 컴포넌트, 디스 바인딩 컴포넌트)을 생성한다.
전역코드 -> 전역 실행문맥 -> 함수코드 -> 함수 실행문맥
이 때, 함수 this binding component
는 암시적으로는 함수를 호출한 객체를 가르킨다. 간단한 예시를 들어보자.
myObj.func()
위 코드에서 func
함수의 this는 myObjs
를 가르킨다. 하지만 myObj
가 사실 객체가 아니라면..? (eg. 단독, 생성자)
함수의 this는 아래 4가지 방법 중 한 가지를 통해 바인딩 된다.
함수를 단독 실행하면 기본적으로 전역 객체에 바인딩 된다.
Browser | |
---|---|
Strict | undefined |
non-Strict | window |
Node | |
---|---|
함수코드 | global |
전역코드 | exports |
단, strict mode
에서는 기본 바인딩은 동작하지 않는다.
함수의 this는 앞서 설명했듯이 메서드를 호출한 객체를 가르키게 된다.
하지만 아래의 경우가 어떻게 동작할지 예상해 보자.
const person = {
name: "seyoung",
function getName() {
return this.name
}
}
function showReturnValue(callback) {
console.log(callback())
}
showReturnValue(person.getName) // undefiend
왜 이 코드의 결과는 undefined
일까?
obj.func
이 .
연산자 혹은 [ ]
연산자를 통해 객체의 프로퍼티에 접근을 하면, 참조 타입이라는 특별한 타입을 반환 해 준다.
참조 타입은 JS에서만 사용되는 타입으로, 객체, 프로퍼티 이름, strict mode 여부에 대해 담고 있다.
obj.getName(): (obj, getName, true)
이때 암시적 this bind은 참조타입의 obj를 통해 해당 객체에 바인딩을 할 수 있는 것이다.
하지만, 위 코드와 같이 참조값을 다른 변수에 담게 되면(e.g. 인수) 해당 프로퍼티의 값만 전달하게 되므로, 참조 객체의 객체에 접근할 수 없게 되는 것이다.
this
의 소실 문제를 해결하기 위해 함수는 call
, apply
, bind
메서드를 제공한다.
call(context, arg1, arg2, ...)
함수의 메서드로 사용하며, 함수의 this에 context를 바인딩한다. 또한, 함수의 인수로 arg1, arg2, ...를 전달받아 호출한다.
apply(context, args)
call
메서드와 똑같이 동작하지만, 인수를 전달받는 방식이 다르다.
bind(context, arg1, arg2, ...)
다른 메서드들이 함수를 직접 호출하는 반면, bind 메서드는 바인딩 작업만 수행한다.
myFunc.bind(context, arg1, arg2)
// myFunc의 this는 context라는 개체로 hard binding된다.
new 생성자를 호출하면 아래와 같이 동작한다.
1. 새로운 객체 생성
2. 함수 코드 실행
3. 새로 생성한 객체 반환
이 때, this는 새로 생성된 객체에 반환된다.
this를 결정하는 방식이 다양한 만큼, 중첩이 발생할 수 있다.
이때 바인딩 우선순위는 아래와 같다.
new > 명시적 바인딩 > 암시적 바인딩 > 기본 바인딩
화살표 함수는 상위 실행 문맥을 유지하기 위해 사용한다. 반면에 this는 함수의 호출 시점에 따라 변하기 때문에, 화살표 함수의 목적과 맞지 않는다. 그렇기 때문에 화살표 함수 내에서 this를 바인딩 할 수는 없고, 가장 가까운 this에 바인딩 된다.