자바스크립트를 공부하다보면 this라는 키워드를 반드시 만나게 된다. 프로그래밍, 객체지향 등 개념을 알고 있는 사람도 자바스크립트만의 독특한 this처리법에 당황하는 경우가 많다. 이번 시간은 자바스크립트에서 this가 어떤 역할을 가지고 있는지 알아보겠다.

this

this는 자신이 속한 객체 또는 생성할 인스턴스를 가리키는 자기 참조 변수이다. 여기까지보면 Python의 self, 자바의 this등과 유사하게 생각된다. 그러나 다음의 이유때문에 자바스크립트에서의 this는 조금 다르게 움직인다. 그것은 바로

  • this값의 바인딩은 함수 호출 방식에 의해 동적으로 결정된다.

이러한 사실때문이다.

흔히 객체지향 프로그래밍을 배우면 자기 참조 변수는 일반적으로 정의된 위치를 따르게 되어있다. 그러나 자바스크립트의 this는 정의된 위치와 상관없이 호출 방식에 의해 결정된다.

저번 포스팅을 봤다면 그 이유를 조금은 이해할 수 있을 것이다.
다음 예제를 살펴보자

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

const b = function() {
  console.log(this);
  a();
}

const obj = { b };
b(); // window, window
obj.b(); // {b: f}, window

첫 번째의 경우 this는 모두 window를 가리키게 된다. 그러나 obj.b();의 경우 첫 번째는 obj, 두 번째는 window를 가리키게 된다.

결론은 위에서 살펴본 것 처럼 this바인딩은 함수를 어떻게 호출하는가에 의해 동적으로 결정된다는 것이다.

b();

위 코드의 경우 b함수를 메소드로서가 아닌 함수 자체로 호출했기에 this는 전역객체인 window가 바인딩된다.

obj.b();

위 코드의 경우 b를 obj의 메소드로서 호출했기에 this는 obj를 가리키게 된다. 그러나 b의 내부에서 a함수를 그냥 호출하고 있기에 a의 this는 obj가 아닌 전역 객체를 가리킨다.

즉 함수가 어디서 정의되었느냐는 this바인딩에 중요한 내용이 아니다. 중요한것은 함수가 어떤 식으로 호출되었느냐다. 다음 예제를 통해 조금 더 살펴보겠다.

const obj = {
 a: function() {
   console.log(this);
 },
 b: function() {
   a();
 }
}

obj.a(); //obj
obj.b(); //window

위 코드에서 a와 b는 모두 obj내부에서 정의되어있는 함수이다. 그러나 일반적인 예상과 다르게 obj.b();의 경우 window가 호출되는것을 볼 수 있다. 이것은 b함수 내부에서 단지 a함수의 호출을 obj.a()가 아닌 a()라고 호출하였기 때문이다.

즉, this는 자기 참조 변수이지만, 자바스크립트에서의 this는 함수 호출 방식에 의해 동적으로 변한다.

this의 종류

자기 참조 변수인 this에는 이렇게 호출 방식에 따라 다양한 객체가 바인딩된다. this에 바인딩되는 객체의 종류를 살펴보자.

전역객체

일반 함수를 호출할 경우 this는 전역객체를 가리킨다. 물론 es6의 화살표함수나 엄격모드인경우는 다르지만, 일반적인 자바스크립트의 함수는 그냥 호출될 경우 this가 전역객체에 바인딩된다.

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

호출한 객체

함수가 객체내부의 메소드로서 호출될 경우 즉, "객체.함수();"의 형태로 호출되는 경우, 함수를 호출한 객체가 this에 바인딩된다. 이는 위에서 살펴본 코드의 예제에서도 동일하다.

생성자 함수가 생성할 인스턴스

생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩된다.

function Rectangle(width, height) {
	this.width = width;
  	this.height = height;
  	this.getArea = function() {
    	return this.width * this.height;
    };
}
const r1 = new Rectangle(5,5);
const r2 = new Rectangle(6,6);
r1.getArea(); // 25
r2.getArea(); // 36

Function.prototype.apply/call/bind 메소드에 의한 간접 호출

apply, call, bind 메소드에 의해 this바인딩을 결정할 수 있다.

function a() {
  return this;
}
const obj = { a: 1 };

console.log(a()); // window

console.log(a.apply(obj)); // obj
console.log(a.call(obj)); // obj

apply와 call 메소드의 본질적인 기능은 함수를 호출하는 것이다. 이들은 함수를 호출하면서 첫 번째 인수로 전달한 특정 객체를 호출한 함수의 this에 바인딩한다.

function a() {
  return this;
}

const obj = { a: 1 };
//함수를 호출하지 않음
console.log(a.bind(obj)); // a
//명시적으로 호출해야 함
console.log(a.bind(obj)()); // obj

bind메소드는 apply와 call 메소드와 달리 함수를 호출하지않고 this로 사용할 객체만 전달한다.

profile
웹 개발을 공부하고 있는 윤석주입니다.

0개의 댓글