[JavaScript] This

HYl·2022년 3월 21일
0

먼저 , MDN 문서에 정의되어 있는 this 를 살펴보자면,

JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작합니다. 또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있습니다.

대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정됩니다. 실행중에는 할당으로 설정할 수 없고 함수를 호출할 때 마다 다를 수 있습니다. ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입했고, ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가했습니다(이는 렉시컬 컨텍스트안의 this값을 유지합니다).


자바스크립트에서 this라는 의미는, 호출한 것을 나타낸다.
따라서 아무런 것을 지정하지 않고 this를 호출하게 된다면 브라우저 환경에서 window가 global 객체 이므로 window가 this가 되는 것이다

console.log(this);

function tempFunc() {
  console.log(this);
}
window.tempFunc();


Class 를 사용하여, 예제를 살펴보자.

counter에서 increase를 호출했으므로, this는 Counter가 된다.

class Counter {
  count = 0;
  increase = function () {
    console.log(this); // this: Counter
  };
}
const counter = new Counter();
counter.increase();

const, let 에 할당하면 ..?

const에 caller를 할당하게되면, this가 undefined가 나오는 것이다.

왜 그런 것일까 ?

원래 Counter에 increase안의 this는 Class인 Counter를 가리키고 있었다,
그런데 caller라는 변수로 할당했기 때문에 this의 정보를 잃어버린 것이다.

let과 const로 선언한 변수는 window에 등록되어져 있지 않으므로,
caller를 호출하는 것은 그 어떤 오브젝트도 아니기 때문에 결과 값이 undefined가 나오는 것이다.

class Counter {
  count = 0;
  increase = function () {
    console.log(this); // this: undefined
  };
}
const counter = new Counter();
const caller = counter.increase();
caller();

잠시 짚고 가자면,

자바스크립트에서 함수를 정의할 때 기본적으로 글로벌 객체에서 접근 가능하다.

function temp() { console.log('temp') }

window.temp();

그러나, const나 let을 이용하여 변수를 선언하게 된다면 윈도우에 등록되지 않는다.
위와 같이 window.temp(); 가 불가능하다는 것이다.

예외 사항이 있다면, 그것은 var

역시나 예외 사항이 존재한다. 바로 var 이다.
const, let과는 달리 var로 함수에 등록하면 위의 코드와 같이 window 객체에 접근할 수 있다.

var는 문제점이 많다. 호이스팅 문제 뿐만 아니라, ( 즉 제일 아래에 선언했는데 상위로 올라가서 선언이 되는 문제. ) 재정의가 되는 다양한 문제점들이 많아서 var을 사용하는 것은 좋지 않다.

여기에서도 또 문제점이 발견되었다. 즉 window에서 var로 정의한 함수에 접근 가능하다는 것.


밑의 예제 코드는,
bob이라는 오브젝트의 run에 있는 함수를, increase를 할당한 것이다.
this 는 Bob이 되는 것을 볼 수 있다.

run이라는 함수는 bob이 불렀기 때문이다.
이처럼 this라는 정보를 다른 곳으로 할당하는 순간 본연의 정보를 잃어버릴 수도 있기 때문에, 위험하다.

그래서 오브젝트와 함수의 관계를 묶어 주기 위하여 bind를 이용한다.

class Counter {
  count = 0;
  increase = function () {
    console.log(this); // this: Bob
  };
}
const counter = new Counter();
counter.increase();
const caller = counter.increase;
caller();

class Bob {}
const bob = new Bob();
bob.run = counter.increase;
bob.run();

bind 사용하기 !

class Counter {
  count = 0;
  increase = function () {
    console.log(this); // this: Bob
  };
}
const counter = new Counter();
counter.increase();
const caller = counter.increase.bind(counter);
caller();

class Bob {}
const bob = new Bob();
bob.run = counter.increase;
bob.run();

일일이 bind를 해주기 귀찮다면 ?

바인딩을 일일이 해주지 않고도 다른 방법을 사용하고 싶다면,
클래스 안에서 함수를 선언할 때 function이 아니라 arrow function을 이용하게 된다면 bind를 이용하지 않아도 연결이 되어진다.
즉, 스코프의 this context를 유지한다.

arrow function 자체에서 scope를 기억하기 때문에 this의 컨텍스를 유지 하기 위해서 쓰이는 함수들은 (bind call apply) arrow function 문법으로 대체할 수 있다.

profile
꾸준히 새로운 것을 알아가는 것을 좋아합니다.

0개의 댓글