본문 바로가기
프로그래밍/JavaScript

[JS] this는 도대체 뭘까? this의 개념과 예시 문제 풀이

by hi-rachel 2023. 1. 17.

자바스크립트 다양한 코드를 보다 보면 계속 this가 문득문득 등장하는데 정확한 개념을 이해하지 못해 답답했다. 계속 등장하는 김에 제대로 알고 가자! 어려운 개념인 만큼 잘 설명한 영상을 찾아봤다.

 

참고 영상

※ 본 포스팅은 아래 영상 내용을 토대로 공부하며 정리해 작성했습니다. 잘 모르겠다면 직접 영상을 보길 추천합니다.

 

✏️ this의 개념

대부분의 경우 this의 값은 ⭐️함수를 호출한 방법에 의해 결정된다.

실행중에는 할당으로 설정할 수 없고 함수를 호출할 때마다 다를 수 있다. ES5는 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 bind 메서드를 도입했고, ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수를 추가했다.

+) 공부해보니 사투리로 이거 저거 뭐든 가리킬 때 하나로 부르는데 아는 사람들은 부르는 게 다 달라도 문맥에 따라 찰떡같이 알아듣는 것처럼, this도 비슷한 개념으로 대명사가 부를 때마다(함수를 호출) 문맥에 따라서(호출 방법에 따라서) 그 의미(값)가 달라진다. 또 예를 들어 거실에서 그거 달라고 할 때 항상 리모컨을 가리킨다고 하면(bind() 메서드로 값을 지정해 주면) 그거의 뜻은(this 값)은 거실에서 항상 동일할 수 있다.

 

값(Value)

실행 컨텍스트(global, function 또는 eval)의 프로퍼티는 비엄격 모드에서 항상 객체를 참조하며, 엄격 모드에서는 어떠한 값이든 될 수 있습니다.

 

전역 문맥

전역 실행 맥락에서 this는 엄격 모드 여부에 관계 없이 전역 객체를 참조한다.

// 웹 브라우저에서는 window 객체가 전역 객체
console.log(this === window); // true

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

this.b = "MDN";
console.log(window.b)  // "MDN"
console.log(b)         // "MDN"

+ (globalThis 프로퍼티를 사용하여 코드가 실행중인 현재 컨텍스트와 관계없이 항상 전역 객체를 얻을 수 있다.)

 

함수 문맥

함수 내부에서 this의 값함수를 호출한 방법에 의해 좌우된다.

 

✏️ 예시

const me = {
  name: "Rachel",
  getName: function () {
    console.log("My Name", this);
  },
};

me.getName(); // My Name {name: 'Rachel', getName: ƒ}

const globalMe = me.getName; // Window
globalMe();

const friends = {
  name: "Monica",
  getName: me.getName, // My Name {name: 'Monica', getName: ƒ}
};
friends.getName();

const btn = document.getElementById("button");
btn.addEventListener("click", me.getName); // button tag => this는 함수가 호출될 때 결정되는데 btn이 호출했으므로 button tag 자체가 this

// this 값을 고정시켜주기 위해 .bind 사용
const bindGetName = friends.getName.bind(me);
bindGetName(); // My Name {name: 'Rachel', getName: ƒ}

console

globalMe에서는 전역 문맥으로 웹 브라우저에서는 Window 객체가 전역 객체이므로 Window 객체를 반환한다.

위 다양한 함수 예시를 보면 함수를 호출하는 방법에 따라 this의 값이 달라지는 것을 알 수 있고 bind()로 값을 고정할 수도 있다.

bind()

bind()메서드는 호출될 때 this키워드가 제공된 값으로 설정되고, 새 함수가 호출될 때는 제공된 인수 앞에 주어진 인수 시퀀스(sequence of arguments)가 있는 새 함수를 만듭니다.

// Syntax
bind(thisArg)
bind(thisArg, arg1)
bind(thisArg, arg1, arg2)
bind(thisArg, arg1, arg2, /* …, */ argN)

 

✏️ 문제 풀이

문제 - 1

const testCar = {
  name: "benz",
  getName: function () {
    console.log("get Name", this); // benz
    const innerFunc = function () {
      console.log("innerFunc", this); // Window 객체가 호출했기 때문에 this는 Window
    };
    innerFunc();
  },
};

testCar.getName();

이 코드에서 this의 값을 같게 하려면 어떻게 고쳐야 할까?

 

문제 - 1 해결 방법

// this 값 값게 해결 방법 1: 화살표 함수 사용
// 화살표 함수에서의 this는 함수가 속해있는 곳의 ⭐️상위 this를 계승받는다.
// 화살표 함수는 bind 메서드를 제공하지 않는다.
const testCar = {
  name: "benz",
  getName: function () {
    console.log("get Name", this); // benz
    const innerFunc = () => {
      console.log("innerFunc", this); // benz
    };
    innerFunc();
  },
};

testCar.getName();

화살표 함수 내에서는 this가 함수가 속해있는 곳의 상위 this를 계승받는다는 것을 기억하자!

 

문제 - 2

const ageTest = {
  unit: "살",
  ageList: [10, 20, 30],
  getAgeList: function () {
    const result = this.ageList.map(function (age) {
      return age + this.unit; // [NaN, NaN, NaN]
    });

    console.log(result);
  },
};

ageTest.getAgeList();

[10살, 20살, 30살] 이렇게 출력하고 싶은데 [NaN, NaN, NaN]이 찍힌다. 어떻게 고쳐야 할까?

 

문제 - 2 해결 방법

// 문제 -2 ⭐️ 해결 방법 1: 화살표 함수 사용
const ageTest = {
  unit: "살",
  ageList: [10, 20, 30],
  getAgeList: function () {
    const result = this.ageList.map((age) => {
      return age + this.unit; // ['10살', '20살', '30살']
    });

    console.log(result);
  },
};

ageTest.getAgeList();

문제 - 1과 동일하게 화살표 함수로 바꿔주면 함수 내부에서 this의 값을 계승받아 잘 출력할 수 있다.

 

// 문제 -2 해결 방안2
const ageTest = {
  unit: "살",
  ageList: [10, 20, 30],
  getAgeList: function () {
    const result = this.ageList.map(function (age) {
      return age + this.unit; // ['10살', '20살', '30살']
    }, ageTest);

    console.log(result);
  },
};

// 문제 -2 해결 방안3
const ageTest = {
  unit: "살",
  ageList: [10, 20, 30],
  getAgeList: function () {
    const result = this.ageList.map(function (age) {
      return age + ageTest.unit; // ['10살', '20살', '30살']
    });

    console.log(result);
  },
};

ageTest.getAgeList();

이런 식으로 해당 함수를 직접 지정해 주어도 올바르게 출력할 수 있다.

 

📌  정리

this를 쓰면 값이 계속 바뀔 수 있는데 this를 쓰려면 어떤 식으로 값을 지정해 주는 것이 좋을까?

 

1. 일반적으로 this를 쓰고 싶다면 일반 함수를 쓰자 => .bind()원하는 객체를 지정해 줄 수 있다. (좀 더 예측 가능)

2. 함수 내에 있는 함수는 상위의 값을 그대로 받는 경우가 많기 때문에 화살표 함수도 괜찮다.

 

대부분의 경우 this의 값함수를 호출한 방법에 의해 결정된다는 사실을 기억하자.

 

 

🙂 공부하면서 정리한 글입니다. 피드백과 공감 환영합니다.