[javascript] this

pds·2023년 3월 31일
0

TIL

목록 보기
44/60

Java에서는 클래스의 인스턴스 본인 참조를 위해 this를 많이 사용하고 있었고 거의 해당 역할로만 사용했었어서 익숙했다.

Javascript를 사용할 때 리액트를 사용하다보니 클래스를 사용할 일이 없었고 그 외에도 this를 사용해본적이 없어서 지금까지 리액트로 개발하면서도 개념을 모르고 있었다.

이번 기회에 Javascript에서의 this에 대해 면밀히 알아보았다.


this

자바나 C, C++ 같은 경우 this는 하나의 인스턴스에 고정되지만, 자바스크립트 또는 타입스크립트는 런타임 상에서 this 바인딩이 동적으로 결정된다.

this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메소드를 참조할 수 있으며

this가 가리키는 값은 다른 언어와는 달리 실행 컨텍스트에 따라 달라진다.

즉 어떤 환경의 어디 스코프에서 사용하냐에 따라 달라진다.

Global Context

Global Context는 환경에 따라 두가지가 있다.

브라우저에서는 전역 객체 window를 가리키며 노드환경에서는 전역 객체 global을 가리킨다.

함수, 객체

함수 내부에서의 this 값은 함수를 호출하는 방법에 의해 좌우된다.

선언적 함수에서의 단순 호출

엄격모드일 경우 undefined이며 엄격모드가 아니라면 기본으로 전역 컨텍스트의 객체를 참조한다.

아래의 경우 this의 값이 호출에 의해 설정되지 않았기 때문에 window객체를 참조한다.

function a() {console.log(this);};
a(); // window

함수 내에서 this는 함수가 호출될 때 결정되는데 선언적 함수는 이미 선언부가 호이스팅을 통해 메모리에 할당되기 때문에 this도 이 시점에 결정되기 때문에 동적 바인딩이 불가능하다.


call, apply, bind 메소드 사용

해당 메소드들을 사용해 선언적 함수에서의 this를 동적으로 결정할 수 있다.

call: 주어진 this값 및 전달된 args와 함께 함수를 호출한다.

func.call(thisArg[, arg1[, arg2[, ...]]])

thisArg 인자로 해당 함수 호출에 제공될 this를 결정한다.

function a() {
  console.log(this);
}

a(); // window
a.call("hell"); // 'hell'
a.apply("he"); // 'he'
a.bind("wa")(); // 'wa';

화살표 함수에서의 호출

화살표 함수에서 this는 자신을 감싼 정적 범위로 전역 코드에서는 전역 객체를 가리킨다.

var globalObject = this;
var foo = (() => this);
console.log(foo() === globalObject); // true

화살표 함수는 call, bind, apply를 호출해 this를 동적으로 지정하려고 해도 무시된다.

let a = () => console.log(this);
a(); // window
a.call('he'); // window

화살표 함수의 this는 실행 컨텍스트로 동적으로 결정되는 것이 아니라 정의되는 시점에 정적으로 결정되며 항상 함수를 둘러싼 가장 가까운 일반 함수this를 상속하기 때문에 동적 지정이 안된다.

화살표 함수 자체에는 this가 없고 본인과 가장 가까운 상위의 this를 사용하는 것이라고 이해했다.

var obj = {
  bar: function() {
    var x = function() {return this};
    return x;
  }
};
obj.bar()(); // window

var obj2 = {
 bar: function() {
   var x = () => this;
   return x;
 }
};
obj2.bar()(); // {bar: ƒ}

객체의 메소드

함수를 어떤 객체의 메소드로 호출하면 this의 값은 그 객체를 사용한다.

const f = {
  a() {
    console.log(this);
  },
  b: function () {
    console.log(this);
  },
  c: () => console.log(this)
};
f.a(); // {a: ƒ, b: ƒ, c: ƒ}
f.b(); // {a: ƒ, b: ƒ, c: ƒ}
f.c(); // window

정의 방법이나 위치에 영향을 받지 않으며 호출하는 시점에 결국 어떤 객체로부터 호출되었는지가 중요하다.

let o = { val: 1 };
function f() {
  return this.val;
}
f(); // undefined;
o.f = f;
o.f(); // 1;
o.ff = { f: f, val: 2};
o.ff.f(); // 2;

생성자

함수를 new 키워드와 함께 생성자로 사용하면 this는 새로 생긴 객체에 묶인다.


function My() {
  this.my = 123;
}
new My().my; // 123;

function My2() {
  console.log(this);
}
new My2();

new 키워드로 인스턴스화 되어 사용되는 함수의 this는 본인을 가리킨다.


DOM 이벤트 처리

함수를 이벤트 처리기로 사용하면 this는 이벤트를 처리해야되는 요소로 설정된다.

<body>
  <button id='btn'>hello</button>
  <button id='btn2'>hello2</button>
<script>
   document.getElementById('btn').addEventListener('click', function() { console.log(this) });
   document.getElementById('btn2').addEventListener('click', () => console.log(this) );   
</script>
</body>

addEventListener의 콜백에 이미 호출되는 요소가 this로 바인딩되도록 정의되어있어

화살표함수를 사용하면 기존 바인딩이 사라지고 상위 스코프로 바인딩된다.


...

음 자바스크립트에서의 this는 정말 헷갈리는 것 같다.

사용을 안하다보니 더욱 와닿지 않는 것 같고 정상적으로 100% 이해하지 못했지만 공부하며 얻은 결론은 다음과 같다.

선언적 함수의 this는 동적이여서 예측이 힘들다. 화살표함수를 사용해 외부 스코프로부터의 this참조를 보장해 예측가능해진다.

객체의 메소드로써 객체를 참조해 무언가를 동작시켜야되거나 이벤트리스너에서 엘리먼트를 참조해야 할 때 정도는 화살표 함수 사용에 주의해야할 것 같다는 생각이 들었다.

Reference

profile
강해지고 싶은 주니어 프론트엔드 개발자

0개의 댓글