This | 디스

this를 왜 사용하는가?

function identify() {
return this.name.toUpperCase();
}
function speak() {
var greeting = `Hello, I'm ${identify.call(this)}`;
console.log(greting);
}
var me = {
name: "Kyle"
};
var you = {
name: "Reader"
};
identify.call(me);
speak.call(you);

this를 안 쓰고 identify, speak 함수에 콘텍스트 객체를 명시할 수도 있다.

function identify(contenxt) {
return context.name.toUpperCase();
}
function speak(context) {
var greeting = `Hello, I'm ${identify(context)}`;
console.log(greting);
}
identify(you);
speak(me);

하지만 암시적인 객체 레퍼런스를 함께 넘기는 this체계가 API 설계상 좀 더 깔끔하고 명확하며 재사용하기 쉽다.

헷갈리는 것들

this는 함수 그 자체를 가리키는 것이 아니다.

function foo(num) {
console.log(`foo: ${num}`);
this.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
console.log(foo.count); //0

console.log에 4번의 foo 함수 호출 횟수가 표시되는데 foo.count 값은 0이다.

foo.count = 0을 하면 foo 함수 객체에 count 프로퍼티가 추가된다.

하지만 this.count에서 this는 함수 객체를 바라보는 것이 아니라 근거지를 둔 객체 자체가 다르다.

실제로는 글로벌 스코프에서 count라는 변수를 찾는다. count 변수를 LHS 검색을 하는데 글로벌 스코프까지 가서 못찾으면 생성을 한다.

하지만 undefined인 상태에서 증감연산자를 사용했으므로 실제로는 NaN이 들어가게 된다.

function foo(num) {
console.log(`foo: ${num}`);
data.count++;
}
var data = {
count: 0
};
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
console.log(data.count); //4

이렇게 렉시컬 스코프를 통해서도 해결은 가능하다.

함수가 내부에서 자신을 참조할때 일반적으로 this만으로는 부족하며 렉시컬 식별자 변수를 거쳐 함수 객체를 참조한다.

function foo() {
foo.count = 4; // foo는 자기 자신을 가리킨다.
}
setTimeout(function() {}, 10);

이름 붙은 함수라 불리는 foo는 foo라는 함수명 자체가 내부에서 자신을 가리키는 레퍼런스로 쓰인다.

하지만 setTimeout에 콜백으로 전달하는 함수는 이름 식별자가 없으므로 함수 자신을 참조할 방법이 없다.

function foo(num) {
console.log(`foo: ${num}`);
foo.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo(i);
}
}
console.log(foo.count); //4

this 없이 함수 객체 레퍼런스로 foo 식별자를 사용해도 문제없이 작동한다.

function foo(num) {
console.log(`foo: ${num}`);
this.count++;
}
foo.count = 0;
var i;
for (i = 0; i < 10; i++) {
if (i > 5) {
foo.call(foo, i);
}
}
console.log(foo.count); //4

foo 함수 객체를 직접 가리키도록 강제해서 this 그대로 적용했다.

자신의 스코프

this는 어떤 식으로도 렉시컬 스코프를 참조하지 않는다는 사실

내부적으로 스코프는 별개의 식별자가 달린 프로퍼티로 구성된 객체의 일종이나 스코프 객체는 자바스크립트 구현체인 엔진의 내부 부품이기 때문에 일반 자바스크립트 코드로는 접근하지 못한다.

function foo() {
var a = 2;
this.bar();
}
function bar() {
console.log(this.a);
}
foo(); // ReferenceError

이 코드는 foo와 bar의 렉시컬 스코프 사이에 어떤 연결 통로를 만들어 bar가 foo의 내부 스코프에 있는 변수 a에 접근하게 하려는 의도 였다.

하지만 그런 연결통론느 없다.

렉시컬 스코프 안에 있는 뭔가를 this 레퍼런스로 참조하기란 애당초 가능하지 않다.

this는 무엇인가?

this는 작성 시점이 아닌 런타임 시점에 바인딩되며 함수 호출 당시 상황에 따라 콘텍스트가 결정된다고 말했다.

함수 선언 위치와 상관없이 this 바인딩은 오로지 어떻게 함수를 호출했느냐에 따라 정해진다.

어떤 함수를 호출하면 실행 콘텍스트가 만들어진다. 여기엔 함수가 호출된 근원과 호출 방법, 전달된 인자 등의 정보가 담겨있다.

this 레퍼런스는 그중 하나로 함수가 실행되는 동안 이용할 수 있다.

정리하기

this는 함수 자신이나 함수의 렉시컬 스코프를 가리키는 레퍼런스가 아니다.

this는 실제로 함수 호출 시점에 바인딩 되며 무엇을 가리킬지는 전적으로 함수를 호출한 코드에 달렸다.