본문 바로가기
Javascript

자바스크립트의 this 알아보기

by East-K 2024. 11. 9.

Javascript에서의 this 란?

MDN에서는 자바스크립트의 this를 아래와 같이 정의하고 있습니다.


JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작합니다. 또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있습니다. 대부분의 경우 this의 값은 함수를 호출한 방법에 의해 결정됩니다. 실행중에는 할당으로 설정할 수 없고 함수를 호출할 때 마다 다를 수 있습니다. ES5는 함수를 어떻게 호출했는지 상관하지 않고` this `값을 설정할 수 있는 bind 메서드를 도입했고, ES2015는 스스로의this바인딩을 제공하지 않는 화살표 함수를 추가했습니다(이는 렉시컬 컨텍스트안의this값을 유지합니다).`


위 설명과 같이 자바스크립트에서의 this는 함수가 호출되는 문맥에 따라 가리키는 대상이 달라지는 키워드입니다. 일반적인 프로그래밍 언어들과 다르게 동작할 수 있으며, 호출 방법에 따라 다르게 결정됩니다. 비엄격 모드와 엄격 모드에서도 this의 동작이 다를 수 있습니다.

this를 제대로 이해하기 위해 자바스크립트에서의 다양한 this 사용 사례를 알아보겠습니다:

 

 

1. 전역 문맥에서의 this

전역에서의 this는 비엄격 모드에서 전역 객체를 참조하며, 브라우저 환경에서는 window가 됩니다.

console.log(this === window); //true
a = 10;
console.log(window.a, this.a, window.a === this.a); // 10 10 true

2. 함수 문맥에서의 this

함수 문맥에서는 함수의 선언 위치가 아닌 호출 방법에 따라 this가 결정됩니다.

function functionThisLogger() {
  console.log(this);
}

const food = {
  name: '김치볶음밥',
  price: 5000,
  print() {
    console.log(`${this.name}의 가격은 ${this.price}원 입니다.`);
  },
  detail: {
    count: 10,
    print() {
      console.log(`${this.count}개 남았습니다.`);
    }
  },
  callThisInFood: functionThisLogger,
}

food.print(); // 김치볶음밥의 가격은 5000원 입니다.
food.detail.print(); // 10개 남았습니다.
functionThisLogger(); // window
food.callThisInFood(); // food

이처럼 this는 함수를 호출한 객체에 따라 food, food.detail, window로 각각 달라집니다. 마찬가지로, 다음처럼 할당한 후에 호출하면 다르게 동작할 수 있습니다.

const callThisInFood = food.callThisInFood;
callThisInFood(); // window

이 경우, 호출 시점this를 참조할 객체가 없으므로 thiswindow를 가리키게 됩니다.

3. 화살표 함수에서의 this

화살표 함수는 일반 함수와 달리 선언된 시점의 상위 스코프에서 this를 상속받습니다. 호출 시점이 아닌 선언 시점에 this가 결정되며, 이후 변경되지 않습니다

const arrowFunctionThisLogger = () => {
  console.log(this);
};

const food = {
  name: '새우볶음밥',
  price: 4000,
  print: () => {
    console.log(`${this.name}의 가격은 ${this.price}원 입니다.`);
  },
  callThisInFood: arrowFunctionThisLogger,
  callThisInFood2() {
    arrowFunctionThisLogger();
  }
}

food.print(); // undefined의 가격은 undefined원 입니다.
arrowFunctionThisLogger(); // window
food.callThisInFood(); // window
food.callThisInFood2(); // window

위 예에서 arrowFunctionThisLogger는 전역 환경에서 선언되었으므로 thiswindow를 가리키고 다양한 방법으로 호출하더라도 변경되지 않습니다.

추가 예시 (잘못된 사용 예)

const getAppleInformation = function() {
  return {
    name: 'Apple',
    stores: [
      { region: '대구', stock: 100 },
      { region: '서울', stock: 200 },
      { region: '부산', stock: 300 }
    ],
    price: 1000,
    getTotalPriceByRegion() {
      return this.stores.map(function(store) {
        return this.price * store.stock;
      });
    }
  }
};

const appleInformation = getAppleInformation();
console.log(appleInformation.getTotalPriceByRegion()); // [NaN, NaN, NaN]

여기서 getTotalPriceByRegion 내부의 this.stores.map은 호출한 객체가 appleInformation기 때문에 문제 없이 동작하지만, map 함수의 콜백 함수 내의 this는 호출한 객체가 별도로 없기 때문에 window를 가리키며, this.priceundefined로 처리되어 NaN이 반환됩니다.

수정 방법

이 문제는 여러 가지 방법으로 해결할 수 있습니다.

수정 1: 화살표 함수 사용

getTotalPriceByRegion() {
  return this.stores.map(store => this.price * store.stock);
}

화살표 함수를 사용하면 선언된 시점의 상위 스코프this를 가르키기 때문에 map 함수의 콜백 함수로 선언이 될 때 this가 결정이 되고, map함수가 호출된 환경의 thisappleInformation이 되면서 정상 동작 하게 됩니다.

수정 2: map의 두 번째 인자인 thisArg 사용

getTotalPriceByRegion() {
  return this.stores.map(function(store) {
    return this.price * store.stock;
  }, this);
}

map 함수의 두 번째 인자 thisArgthis를 명시하여 값을 설정할 수 있습니다.

수정 3: bind 사용

getTotalPriceByRegion() {
  return this.stores.map(function(store) {
    return this.price * store.stock;
  }.bind(this));
}

수정2와 비슷한 방법으로 함수의 bind 메소드를 통해 this값을 명시하여 값을 설정할 수 있습니다. 이 때 주의해야 할 점은 동일한 함수에 여러 번 bind를 할 경우 가장 처음 bind한 값으로 고정된 후에 변경되지 않는다는 점 입니다.

 

결론

위에 작성한 내용 외에도 thisapply 메소드 등 여러 방법으로 설정할 수 있습니다. 자바스크립트의 this는 어렵게 생각하면 정말 어려운 개념이지만 일반 함수는 호출 시점에 따라 호출한 객체로 this가 동적으로 변경되고, 화살표 함수는 선언 시점의 환경에서 this를 상속받아 고정된다는 점만 기억해도 대부분의 상황에서 올바르게 이해할 수 있을 것입니다.

 

위 내용 관련해서 궁금한 점이 있거나 잘못된 부분이 있다면 댓글 부탁드립니다.