JavaScript 에서 제일 헷갈리는 부분은 바로 이 this가 아닐까?

JavaScript를 첫 언어로 접하신 분에게는 당최 정리가 안되는 이상한 개념이고, 이미 OOP의 개념이 익숙하신 분에게는 도대체 뭐가 뭔지 모르겠는 기괴한 개념인 것 같다.

JavaScript에서 this를 이해하기 위해서는 Call Stack과 Call Site를 이해해야 한다.

Call Stack

마치 C나 C++에서의 공부하였던 Call Stack과도 개념이 완전히 동일한 이 녀석은, JavaScript에서도 그대로 적용된다.

Call Stack은 함수가 호출되는 스택을 의미한다. 스택이라 함은, 뭔가 쌓여있다는 의미이다.

위의 그림은 인터넷에서 가져온 그림인데, C#에서의 Call Stack을 잘 나타내고 있다. Main 함수를 기준으로, Method1이 호출되고, Method1 함수 내에서 Method2 함수가 호출되고, … 반복하여, 그렇게 Method5가 호출된다.

Method5 함수의 실행이 종료되어 값을 반환(return) 하여 Method4로 돌아갈 때 돌아가기 위한 주소를 저장해놓은 Call Stack을 확인하고, Method4로 돌아가서 마저 실행하고, 실행이 종료되면 값을 반환하여 Call Stack을 참조하여 Method3으로 돌아가고, … 반복하여, Main 함수로 돌아온다.

JavaScript에서도 정확히 동일한 개념으로 동작한다. 타 언어와 차이점이 있다면, 이 Main 함수라는 것이 조금 모양이 다를 뿐이다.

브라우저에서의 JavaScript

브라우저에서는 window 라는 global object가 존재한다.

JavaScript 는 모든 것을 object로 다룬다는 것을 명심해야 한다. 여기에서 object는 OOP 에서의 object가 아니라 JavaScript Object 타입, key-value 쌍을 갖는 JavaScript의 데이터 타입이다.

window object 에서 함수도 정의되고 변수도 정의되고, 실행된다. 브라우저 내에서 동작하는 JavaScript의 경우, 결국 모든 것들은 window 객체 아래에 존재하는 것이다. 즉 Main 함수의 역할을 하는 것을 window 객체라고 볼 수 있는 것이다.

Node에서의 JavaScript

Node.js의 런타임에서는 마찬가지로 global 이라는 global object가 존재한다.

브라우저와 마찬가지로, Node에서는 global 객체가 Main 함수의 역할을 한다고 볼 수 있다.


위에서 눈치를 채셨겠지만, JavaScript는 자꾸 object를 다룬다. 만약 함수가 호출이 되면, 호출된 함수가 포함된 객체가 있을 것이다. 대개는 그 객체가 global object 이므로, 이 경우 this는 그 global object가 된다. 브라우저의 경우 window, Node의 경우 global이 되는 것이다.

Call Site

Call Site는 함수가 호출된 지점을 의미한다. 바로 윗 문단에서 언급하였듯, JavaScript에서는 함수가 호출되는 지점에서의 포함된 객체를 찾아 해당 객체와 this를 바인딩한다.

예시1.

var obj1 = {
  item: 'item from obj1',
  mtd: function() {
    console.log(this.item);
  }
}

var obj2 = {
  item: 'item from obj2',
  mtd: obj1.mtd
}

obj2.mtd();		// 'item from obj2' 를 출력한다.

예시2.

var item = 'global item!';

var obj = {
  item: 'item from obj',
  mtd: function() {
    console.log(this.item);
  }
}

var mtd = obj.mtd;

mtd();			// 'global item!' 을 출력한다.

예시3.

function func1() {
  console.log(this.item);
}

function func2(fn) {
  fn();
}

var obj = {
  item: 'item from obj',
  mtd: func1
};

var item = 'global item!';

func2(obj.mtd);	 // 'global item!'을 출력한다.

bind 메서드

bind 메서드는 this를 강제로 bind 메서드를 호출한 함수로 바인딩시킨 후 새로운 함수를 반환하는 메서드이다.

bind 메서드의 원형은 Function.prototype.bind 이므로, 모든 함수는 이를 호출할 수 있다.

예시.

function speakNation() {
  console.log(this.nation);
}

var person = {
  name: 'Hong Kil Dong',
  nation: '한국'
}

var hongSpeakNation = speakNation.bind(person);

hongSpeakNation();

apply 메서드

apply 메서드는 bind와 유사한 일을 하는데, 차이점이 있다면 새로운 함수를 반환하는 것이 아니라 바로 해당 함수를 호출한다.

apply 메서드의 원형은 Function.prototype.apply 이므로, 모든 함수는 이를 호출할 수 있다.

예시.

function speakNation() {
  console.log(this.nation);
}

var person = {
  name: 'Hong Kil Dong',
  nation: '한국'
}

speakNation.apply(person);

call 메서드

call 메서드는 apply와 딱 한가지 차이점을 빼 놓고 완전히 동일하다. call 메서드는 여러 개의 파라미터를 받는 반면, apply하나의 파라미터 리스트 를 받는다.

call 메서드의 원형은 Function.prototype.call 이므로, 모든 함수는 이를 호출할 수 있다.