자바스크립트를 통해 코드를 작성을 하다보면 this를 사용할 일이 있다. 이때, this키워드에 대한 이해가 부족하면 작성된 코드에서 자신이 원하는 결과를 얻지 못할 수도 있다. 자바스크립트에서 this키워드는 다른 언어와 조금 다르게 동작하기 때문에, 확실한 이해가 필요하다.

this

자바스크립트 코드의 실행 컨텍스트에서, 자바스크립트 런타임은 스택을 관리하고 유지한다. 그리고 이 스택의 맨 위에는 현재 실행중인 컨텍스트가 들어가 있을 것이다. this는 이러한 실행 컨텍스트가 변경될 때마다 변경된다. 또한 this를 이용해 함수나 메소드를 호출할 때 그 함수나 메소드의 주체가 되는 객체를 설정할 수 있다. 즉, this는 컨텍스트나 함수들을 재사용할 수 있게 해준다. 앞서 this는 실행 컨텍스트가 변경될 때마다 변경된다고 했는데 그렇다면, this가 현재 무슨 객체를 바라보고 있는지 어떻게 알까? 기본적으로, 이 함수가 어디에서 호출이 되고 있는지를 파악하면 된다. 코드를 통해 살펴보자.

 

Global Context

function foo () {
	console.log(this === window); // true
}

foo();
console.log(this === window) // true

 

Global Context에서 this는 default로 전역 객체를 가리키고 있다. 위의 코드에서 browser의 전역 객체가 window객체이므로 두 개의 console.log에서 true가 출력이 된다. 그리고 NodeJS환경에서의 전역 개체는 window가 아닌 global이다.

 

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

 

위와 같이 익명 함수에서도 마찬가지로 적용된다.

 

use Strict

'use strict';라는 문자열을 삽입하면 엄격 모드가 적용된다. 엄격 모드에서는 thisundefined로 표시된다.

 

function foo () {
	'use strict';
	console.log(this === window); // false
	console.log(this === undefined); // true
}

foo();

 

Implicit Binding

객체의 프로퍼티로 접근하는 암시적 바인딩의 경우를 살펴보자.

 

const movie = {
  name: 'LaLaLand',
  actor: 'Ryan Gosling',
  getName() {
    console.log(this.name);
  }
}

 

위와 같은 객체가 있다고 하자. 우리는 movie객체의 메소드인 getMovie에 접근하기 위해 movie.getMovie()와 같이 .을 사용할 것이다. 이렇게 객체 프로퍼티로 접근을 할 경우 this는 해당 객체를 바인딩한다. 즉, 암시적 바인딩에서 this.좌측에 있는 객체를 가리키게 되고, 위의 경우에서 this.name을 movie.name이라 해석한다.

 

movie.getName(); // LaLaLand

 

그렇다면 다음의 경우는 어떨까?

 

const movie = {
  name: 'LaLaLand',
  actor: 'Ryan Gosling',
  getName() {
    console.log(this.name);
  },
  director: {
    name: 'Damien Chazelle',
    getName() {
      console.log(this.name);
    }
  }
}

movie.getName();
movie.director.getName();

 

movie객체 안에 director객체가 들어가 있다. 위에서 movie.getName();이 LaLaLand를 출력한다는 것은 알았다. 그렇다면 movie.director.getName()은 무엇을 출력할까. getNamethis.좌측의 객체인 director를 참조하게 되고, Damien Chazelle를 출력하게 된다. 또 하나의 예를 보자.

 

function foo () {
	'use strict';
	console.log(this.count);
	console.log(this === window); 
}

let user = {
	count: 10,
	foo: foo,
	foo1: function() {
		console.log(this === window);
	}
}

user.foo(); // 10, false
let fun1 = user.foo1;
fun1(); // true
user.foo1(); // false

 

각각의 경우를 살펴보면, user.foo()에서는 foothisuser객체를 가리키게 되므로 10과 false를 출력하게 된다. 그리고, user.foo1fun1에 담고 fun1을 호출하면 true가 출력이 되는 것을 볼 수 있다. 이렇게 함수에 객체를 바인딩했지만, 다른 변수에 담게 되면 일반 함수로 해석되어 this는 전역 객체를 가리킨다.

 

new Binding

함수가 new를 통해 호출이 되면, 새로운 객체를 반환하고 this는 그 새로운 객체를 가리킨다.

 

function Person(fn, ln) {
	this.first_name = fn;
	this.last_name = ln;

	this.displayName = function() {
		console.log(`Name: ${this.first_name} ${this.last_name}`);
	}
}

let person = new Person("ginger", "kang");
person.displayName();  // Name: ginger kang
let person2 = new Person("javascript", "this");
person2.displayName();  // Name: javascript this

 

이 밖에도, call(), apply()bind()를 통해 this가 가리키는 객체를 명시할 수 있는 명시적 바인딩이 있다. 이는 다음 포스팅에서 다룰 예정이다.

 

참고자료

MDN, https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this

Pavan, https://medium.com/better-programming/understanding-the-this-keyword-in-javascript-cb76d4c7c5e8

 

 


생강강

,