책정리/Deep Dive Javascript

자바스크립트 this 동적 바인딩 총정리

뽀글보리 2023. 11. 27. 08:22
반응형

자바스크립트는 객체지향 프로그래밍 언어이기 때문에, 자신이 속한 프로퍼티나 메서드를 참조할 수 있는 this 식별자를 사용할 수 있어야 한다.

// 객체 리터럴 생성
const circle = {
    radius: 10,
    getDimeter() {
        return 2 * this.radius;
    }
}

console.log(circle.getDiameter); // 20

// 생성자 생성, new로 객체 생성 시 인스턴스 this를 암묵적으로 반환한다.
function Circle(radius) {
    // this : 생성자 함수가 생성할 인스턴스
    this.radius = radius;
}

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

const circle = new Circle(10);
console.log(circle.getDiameter()); // 20

 

다음은 객체 리터럴 방식과, 생성자 방식을 사용해서 circle이라는 객체를 생성하는 두 가지 방식을 모두 표현해봤다. 동일하게 raidus와 getDiameter 속성을 가지는 객체를 만들 수 있다. 이 때, 각 인스턴스를 접근하는 방식은 this를 동일하게 사용할 수 있다.


 

여기까지는 다른 언어와도 유사하고, 모두가 알고있는 this의 사용방식일 것이다. 그러나 자바스크립트의 this는 함수가 호출되는 방식에 따라서 동적으로 결정된다는 점이 특이하다.

 

전역에서의 this

console.log(this);
  • 전역에서의 this는 전역객체 window를 가리킨다.
  • strict mode가 적용될 경우에는 undefined를 가리킨다.

 

일반함수에서의 this

function func() {
    console.log(this);
    return "hi";
}
  • 생성자 함수가 아닌 일반 함수에서의 this는 전역객체 window를 가리킨다.
  • strict mode가 적용될 경우에는 undefined를 가리킨다. 이는 일반 함수에서 this를 사용할 이유가 없기 때문이다.

 

생성자함수에서의 this

function Circle(radius) {
    this.radius = radius;
    console.log(this);
}
  • 함수에서 new 연산자를 사용하면 생성자함수로 동작한다.
  • 생성자 함수 내부에서 this는 생성자 함수가 생성할 인스턴스를 가리킨다.

 

그러나 같은 함수도 일반 함수, 생성자 함수, 메서드 호출로 다르게 호출될 수가 있는 데, 이때 가리키는 this는 동적으로 결정되기 때문에 모두 다를 수 있다.

const func = function () {
  console.dir(this);
}


// 1. 일반 함수 호출
func();

// 2. 메서드 호출
const obj = { foo };
obj.func();

// 3. 생성자 함수 호출
const obj2 = new func();

 

func이라는 함수를 각각 일반 함수 호출, 메서드 호출, 생성자 함수 호출과 같은 다른 방식으로 호출한 경우이다. 1번에서의 this는 window, 2번에서의 this는 obj, 3번에서의 this는 obj2를 가리킬 것이다.

 

여기에서 주의할 점은 메서드 내의 일반 함수도 window를 가리킨다.

const circle = {
    radius: 10,
    foo() {
        console.log(this); // 현재 객체
        setTimeout(function () {
            console.log(this); // window
           }, 100);
    }
}

 

circle 객체 내 foo라는 메서드 함수에서 중첩 함수 또는 콜백 함수 사용을 위해 일반 함수를 호출할 경우에는 전역 객체 window가 바인딩된다. 이는 조금 이상해보인다. 또, 객체외부에서 메서드를 호출할 때 버그를 만들기 쉽다. 그렇다면 이러한 경우에 this 바인딩을 바꾸고 싶다면 어떻게 해야할까?

 


that 사용

const circle = {
    radius: 10,
    foo() {
        const that = this;
        setTimeout(function () {
            console.log(that);
           }, 100);
    }
}

 

bind를 사용하여 명시적으로 this를 바인딩

const circle = {
    radius: 10,
    foo() {
        setTimeout(function () {
            console.log(this);
           }.bind(this), 100);
    }
}

 

화살표 함수 사용

const circle = {
    radius: 10,
    foo() {
        setTimeout(() => {
            console.log(this);
           }, 100);
    }
}

 

메서드 내의 중첩된 일반 함수에서 this가 window가 아닌 객체를 가리키게 변경하는 데에는 다음과 같은 3가지 방법이 있다. 주로 사용의 편의성 때문에 화살표 함수를 많이 사용한다.

 

반응형