# this

자바스크립트에서는 특이하게도 this가 원하지않는대로 작동할수도있다.

대부분의 경우 this의 값은 함수를 호출한 방법이 결정한다. (this에 바인딩할 객체가 동적으로 결정된다.)

그래서 호출 컨텍스트라는 용어가 맞는것같지만.. 함수컨텍스트라는 용어를 쓴다.

function a() {
  this.p1 = 1;
  this.p2 = 2;
  console.log(this);
}

const A = a();
const B = new a();

console.log(A); // undefined
console.log(B); // {p1:1, p2:2}

첫번째로 함수가 constructor로 작동할때, 그렇지않을때의 차이이다.

a함수의 리턴값이 없으므로 A의 경우는 undefined이고,

B의 경우에는 new 연산자를 사용하였으므로 constructor로 작동하면서

인스턴스 객체의 프로퍼티가 보여진다.

두번째로 A의 경우, a함수 내부의 this는 global객체이다.

위의 예제가 일반적이지만 엄격모드(use strict)에서는 this는 글로벌객체가 아닌 undefined를 나타낸다.

세번째로, 전역 실행 문맥(global execution context)에서는 this는 global객체이다.

네번째로, 함수를 어떤 객체의 메서드로 '호출'하면 this의 값은 그 객체를 사용한다.

const obj = {
  p1: 1,
  getP1() {
    return this.p1;
  }
};
obj.getP1(); // 1

호출이라는 단어에 항상 조심해야한다.

위 코드에 이어서 아래 코드를 보자.

const fn = obj.getP1;
fn(); // 1이 아님

fn을 호출하면 window.p1 값으로 평가된다.

이처럼 호출할때 값이 결정된다.

다섯번째로, apply, call, bind, arrow function.

Function.prototype.apply
Function.prototype.call
Function.prototype.bind

위 세가지 메소드는 첫번째 파라미터로 this로 설정할 객체를 넘기면 된다.

참고: Function.call.bind 관련 재밌는 글

그럼 해당 메소드의 this는 넘긴 객체로 동작한다.

arrow function은 this를 바인딩하지않는다. => 상위스코프의 this를 가리킨다.

마지막으로, 메소드 내부에 함수가 또 있으면 그 내부의 this는 또 다르다. 두번째랑 같은 맥락이다.

const obj = {
  p1: 1,
  getP1: function() {
    function someFunction() {
      console.log(this); // global
    }
    return this.p1;
  }
};
obj.getP1(); //1

다른 언어에 익숙한 사람은 function대신 arrow function을 사용하는게 원하는대로 작동해서 정신건강에 이롭다. (객체의 메소드로 쓰면 좋지않고, 생성자로는 쓸수없다.)

혹은 ES5 스펙의 Function.prototype.bind를 써도 좋다.


# this와 관련해서..

실제로 메소드의 별칭을 만들어서(메소드를 외부 변수에 할당) 사용하는 것은 매우 위험하다.

js의 특성상 this의 바인딩이 늦다. 이 말은 메소드를 할당한 변수를 호출했을때,

내부의 this가 더이상 원래 가리키던 객체가 아니게 된다.

설명한것은 아래의 코드와 같다.

const someObj = {
  a: 1,
  someMethod() {
    return this.a;
  }
};

console.log('someObj.someMethod', someObj.someMethod()); // 1
const v1 = someObj.someMethod;
console.log('v1', v1()); // undefined