본문 바로가기

JavaScript

[JavaScript] This...This...This!!

다른 언어에선 쉬울 수 있으나, JavaScript에서의 this란 정말 정말 난해하다.

 

 

this의 본 의미는 "자기를 참조하는 변수" 이다.  생성자 함수를 예로 들어보면, 생성자 함수를 정의하는 시점에서는 아직 인스턴스를 생성하기 전이기 때문에 자신이 속한 객체나 생성할 인스턴스를 가리키는 식별자가 필요하다. 이를 위해서 존재하는 것이 this이다.

 

 

즉, this는 자신이 속한 객체나 자신이 생성할 인스턴스의 프로퍼티 혹은 메서드를 참조할 수 있다. 

 

 

그렇다면 this는 어떤식으로 동작할까?

 

기본적으로 JavaScript의 this는 함수 호출 방식에 의해서 동적으로 결정된다. 식별자와 값을 연결하는 과정을 binding이라고 하는 데, JavaScript에서는 크게 4가지 방식이 존재한다.

 

 

const circle = {
    radius: 5,
    getDiameter(): {
    	return 2 * this.radius;
    }
};

console.log(circle.getDiameter());

정답을 맞춰보세요!(드래그) : 10

 

 

이 예제에서 알 수 있듯이 객체 리터럴의 메서드 내부에서의 this는 메서드를 호출한 객체를 가리킨다.

 

 

function Circle(radius) {
    this.radius = radius;
}

Circle.prototype.getDiameter = function () {
    return 2 * this.radius;
};

const circle = new Circle(5);
console.log(circle.getDiameter());

정답을 맞춰보세요!(드래그) : 10

 

 

2번째 예제에서는 생성자 함수를 사용하였다. 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.

 

 

 

그럼 다른 상황을 살펴보자.

 

 

기본적으로 this를 호출하면 그 this는 전역을 가리킨다. (Window 혹은 global)

 

그렇다면 일반적인 함수는 어떨까?

 

function foo() {
    console.log(this);
    function bar() {
        console.log(this);
    };
    bar();
};

foo();

 

일반 함수로 호출시에는 중첩 함수든 전역 함수든 모두 전역 객체가 바인딩된다. 따라서 일반 함수에서 this는 큰 의미가 없다. 이는 콜백 함수가 일반 함수로 호출될 때에도 마찬가지이다. (setTimeout)

 

 

 

메서드 내부의 this는 메서드를 호출한 객체를 가리킨다.

 

const person = {
    name: "Jang",
    getName() {
        return this.name
    }
};

console.log(person.getName());

 

 

여기서 console.log의 결과는 "Jang"이 나온다. 여기서 getName()은 person 객체에 포함되지 않고 독립적은 함수 객체로 존재하며, getName 프로퍼티가 함수 객체를 가리키고 있기 때문에 name이 바인딩이 된 것이다.

 

 

여기서 알 수 있듯이, 메서드 내부 this는 메서드를 호출한 객체에 바인딩 된다. (프로토타입 메서드 내부도 마찬가지이다.)

 

 

 

생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스가 바인딩된다. (위 circle 예제 참조), 단 주의할 점은 new 연산자와 함께 호출하여야 해당 함수가 생성자 함수로 동작한다는 것이다.

 

 

 

Function.prototype.apply/call/bind 메서드 (간접 호출)

 

그 외에도 ES5에 있는 apply, call, bind 메서드를 통해 this를 간접적으로 호출할 수 있다. 이 메서드들은 this로 사용할 객체와 인수 목록을 프로퍼티로 전달받아 함수를 호출한다. 이들의 원래 기능은 함수를 호출하는 것이기 때문이다.

 

 

위 목록들을 정리하자면.

일반 함수 전역 객체
메서드 호출 메서드를 호출한 객체
생성자 함수 호출 생성자 함수가 생성할 인스턴스
apply / call / bind 이 메서드의 첫번째 인수로 전달한 객체

 

 

 

문제!

 

function test() {
  console.log(this.b);
}

var obj1 = {
  b: 10,
  func: test
};

var obj2 = {
  b: 40
};

obj2.func = obj1.func;
obj2.func(); // 1

다음의 결과를 맞춰보세요! 1. 40