JS에서 this

낭만개발자·2021년 11월 2일
0

JavaScript

목록 보기
6/14

java로 코딩을 시작하고 js로 넘어간 나같은 사람은 this에서 엄청난 혼란을 겪는다. java는 기억으론 this가 인스턴스의 멤버로 사용되고 생성자에 있을땐 전역으로 사용된다 정도로 알고 있으면 되었지만 js는 함수 개념으로서 다양하게 쓰이기 때문임.

js의 this는 함수를 호출할 때 함수가 어떻게 호출되었는지에 따라 this에 바인딩 될 객체가 동적으로 결정된다.

1. (기본)함수에서 호출 일때

기본적으로 this는 전역객체에 바인딩된다.

일반 함수는 물론이고, 내부함수, 콜백함수도 this를 사용하면 전역 객체에 바인딩 된다.

즉 함수가 메서드 형식이 아니라면(=어떤 객체의 프로퍼티로 정의되어 있지 않다면)
window 에 바인딩 된다.

function foo() {
  console.log("foo's this: ",  this);  // window
  function bar() {
    console.log("bar's this: ", this); // window
  }
  bar();
}
foo();

일반 함수 foo()의 this는 js의 기본적인 전역 객체인 window에 바인딩 되는 걸 알 수 있다.
bar()의 경우도 window에 바인딩된다. 혹 나처럼 전역 객체면 함수도 객체니 foo() 에 바인딩 되어야 하는거 아닌가? 생각하는 사람도 있을수 있으나, 여기서 말하는 전역객체에선 상위 함수를 전역 객체로 보지 않는 것 같다. 아래 나오는 것과 같이, 객체 내에서 property로 정의된 함수! 즉 메서드 형식으로 정의된 함수에서 전역객체가 window가 아닌 상위 객체로 의미 되어진다.

var value = 1;

var obj = {
  value: 100,
  foo: function() {
    console.log("foo's this: ",  this);  // obj
    console.log("foo's this.value: ",  this.value); // 100
    function bar() {
      console.log("bar's this: ",  this); // window
      console.log("bar's this.value: ", this.value); // 1
    }
    bar();
  }
};

obj.foo();

뭔말인가 하면 위에 첫번째 this가 속한 익명 function은 일반 함수로서 호출! 된 것이 아니라 메소드로서 호출! 된 것이다. 메서드란 보통 class 내에 멤버로 소속된 함수를 메서드로 통칭하는걸로 아는데, 그런 개념이다. 그래서 메서드로서 호출되면 this가 가리키는 전역은 더이상 window가 아닌 객체 obj 이 된다.
그래서 위와 같이 첫번째 this는 obj를 가리키고, 세번째 this는 함수내의 함수: 내부함수 이므로 내부함수의 전역객체는 여전히 window가 된다.
(함수내의 함수이면 전역객체는 window고, 함수 내의 프로퍼티에 정의된 함수면 전역객체가 window에서 자기가 소속된 객체로 변경된다.)

다시 한번 위 예제를 좀더 심화 하자면

내가 3행부터 let test1을 만들어 보았다. 그래서 A의 경우는 let test1이 없으면 원래 전역 객체 obj를 가리키는게 맞는데 obj.foo가 프로퍼티로 메서드 형식으로 test1에 속해 있어, 결국엔 전역객체는 let test1이 된다.
B,C의 전역객체는 당연 window다.

즉 객체 내부의 property로 함수가 묶여있을때만(이걸 메서드 형식으로 불리는 것 같다.) 전역객체가 window => 뭐시기 로 변하므로 this가 뭐시기로 변한다는걸 주의해주고, 콜백함수, 내부함수, 프로퍼티로 안속한 일반함수 경우 this는 window를 가리킨다고 알면 된다.

위 방법 이외에도 자바스크립트는 this를 명시적으로 바인딩할 수 있는 apply, call, bind 메소드를 제공한다 라고 poemaweb.com엔 나와있다.
apply, call, bind 다 사용 가능하지만 bind에 대해서만 공부해봤다. 밑줄친 bind가 의미하는 건
bar함수에서 this를 사용할땐 무조건 obj를 가르키게 해! 라는 의미 즉 바인딩 시켜라는 의미로 쓰인다.
뒤에 (1, 2) 는 즉시 실행 함수라고 bind(obj)까지 설정해놓고, 바로 즉시 함수를 call해보고 싶을때 bar(1,2); 쓰기 귀찮으니 단축해서 (1,2)로 사용하는 것이다.
초보자 개념 설명 페이지에 이렇게 즉시함수 같은거 사용하고 설명도 안달아 두는것 보면 친절한 책은 아닌듯..

2. 메소드 호출

앞에서 다 설명했다. 객체 프로퍼티로 등록된 함수 (소위 말하자면 메소드호출 형식) 는 this가 전역객체를 똑같이 가르키는데, 이 경우 전역객체가 window에서 다른걸로 변경된다.

위에 obj1.sayName() 의 this는 obj1을 가르키고
위에 obj2.sayName() 의 this는 obj2를 가르킨다.

프로토타입 객체 내부에 사용된 this도 일반 메소드 방식 처럼 해당 메소드를 호출한 객체에 바인딩 된다.
아래는 getName에 있는 this는 해당 메소드 getName을 호출한 객체 : me에 바인딩이 된다.
me는 무엇이냐면, Person()이라는 함수에 'Lee'라는 parameter를 넣은 인스턴스이다. 즉 새로운 인스턴스 이므로 function Person(name){ 은 java처럼 생성자로서 내부의 this는 기본적으로 Person을 가르키고 있다가, 인스턴스가 생성되면서 me 인스턴스 객체를 가르키게 된다.
즉 Person('Lee')의 인스턴스가 생기고, this.name에 의해 'Lee'값은 인스턴스 내의 전역 변수 name의 값으로 퍼지게? 되고, me.getName()을 출력하게 되면 5행의 Person.prototype.getName 함수의 this.name은 아까 퍼졌던 name 값을 받아 Lee를 출력하게 된다.

즉 프로토 타입 형식으로 정의 된것들도 메소드이므로 this는 prototype으로 묶인 상위 전역 객체를 가르킨다.

예외 : 화살표 함수로 메소드를 정의해도 화살표 함수의 this 는 상위컨택스트인 window를 가르킨다. 따라서 화살표 함수는 메소드 정의할땐 피해야 한다.

// Bad
const person = {
  name: 'Lee',
  sayHi: () => console.log(`Hi ${this.name}`)
};

person.sayHi(); // Hi undefined

출처 : this : https://poiemaweb.com/js-this
화살표 함수 : https://poiemaweb.com/es6-arrow-function#3-this

profile
낭만닥터와 슬의를 보고 저런 개발자가 되어야 겠다고 꿈꿔봅니다.

0개의 댓글