그럼에도 불구하고

👨‍💻

[JavaScript] this에 대해 알아보자 본문

JavaScript/JavaScript basics

[JavaScript] this에 대해 알아보자

zenghyun 2022. 12. 30. 19:19

오늘은 this에 대해 기록해보고자 한다.

 

 

 

이론적인 부분에서 공부할 때 너무 어려운 부분이 많았다. 

그래서 이해가 안되는 부분이 많았는데 부족할 수 있지만 이번 계기로 확실히 정리해 보자 

 

결론부터 말하자면 

1. this는 함수가 호출될 때 결정이 된다.

 

2. 화살표 함수에서의 this는 함수가 속해있는 곳의 상위 this를 계승받는다.

 

 

실습으로 내가 이해한 대로 적어봐야겠다.

 

ver_1

<script>
const car = {
      name : 'KIA',
      getName : function() {
        console.log("car getName", this);
      },
    };

    //car.getName(); // A.b
    
const globalCar = car.getName;
    globalCar();       // b    
    
</script>

car라는 객체는 name 값으로 "KIA"를 가지고 있다. 

여기서 car.getName(); 으로 car라는 객체의 getName() 메서드를 호출했다.

 

this는 함수가 호출될 때 결정이 된다. 

 

즉,  car.getName();는 car가 getName()을 호출한 것이다. 

이때 getName() 안에 있는 this는 자신을 호출한 객체를 가리킨다.

 

결과 :

 


 

 

ver_2

<script>
const car = {
      name : 'KIA',
      getName : function() {
        console.log("car getName", this);
      },
    };

    //car.getName(); // A.b

    const globalCar = car.getName;
    globalCar();       // b
</script>

 

globarCar();를 호출하면 어떤 값이 나올까? 

this는 함수가 호출될 때 결정이 된다고 했다. 

 

그렇다면 car.getName도 첫 번째 예시와 같은 값이 나올 것 같지만 그렇지 않다. 

 

 

결과 :

 

뜬금없이 Window 내장 객체가 나온다. 

이건 ver_1과 다르다.

ver_1의 경우는 car.getName();로 car가 getName()을 호출했지만,

ver_2의 경우는 car.getName()이라는 값을 globalCar라는 변수에 초기화한 것이다. 

 

즉,   const globalCar = car.getName;에서 globalCar가 car.getName(); 을 호출한 셈이다. 

그렇기 때문에 전역변수로 선언된 globalCar에 맞춰 this는 Window 객체를 가리키는 것이다.

 


 

ver_3

<script>
 const car = {
      name : 'KIA',
      getName : function() {
        console.log("car getName", this);
      },
 };
 
 const car2 = {
      name :"hyundai",
      getName : car.getName,
    }; 
    car2.getName(); 
</script>

 

car2라는 객체는 getName이라는 이름으로 car 객체의 getName 메서드를 호출하고 있다. 

 

this는 함수가 호출될 때 결정이 된다고 했다. 

car2가 getName()을 호출하고 있기 때문에 여기서 this는 car2를 말한다. 

 

결과:

 


 

ver_4

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button id="button">click</button>
  <script>

    const car = {
      name : 'KIA',
      getName : function() {
        console.log("car getName", this);
      },
    };

    //car.getName(); // A.b


   const btn = document.getElementById("button");
   btn.addEventListener('click', car.getName); 
    
 </script>
</body>
</html>

btn이라는 변수는 button이란 id 선택자를 가진 button태그이다. 

여기서 btn 즉, button을 클릭했을 때 car.getName의 this는 무엇을 가리킬까? 

 

결과:

 

신기하게도 button 태그 자체를 가져와서 출력한다.

this는 함수가 호출될 때 결정이 된다고 했다. 

 

 

 btn.addEventListener('click', car.getName); 은 btn이라는 button을 클릭했을 때 발생하는 이벤트이다. 

그 이벤트는 car.getName을 호출하며, 호출을 하는 대상은 btn이다.

 

그렇기 때문에 <button id="button"> click </button>을 출력하는 것이다.

 


 

ver_5

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <button id="button">click</button>
  <script>

    const car = {
      name : 'KIA',
      getName : function() {
        console.log("car getName", this);
      },
    };

    //car.getName(); // A.b


   const btn = document.getElementById("button");
   btn.addEventListener('click', car.getName.bind(car)); 
    
 </script>
</body>
</html>

 

ver_4와 바뀐 점은 btn.addEventListener('click', car.getName.bind(car));  에서 bind()라는 함수가 호출된 것이다. 

 

※ bind()

bind() 함수는 this의 값을 고정시키기 위한 함수이다. 

즉, bind() 안에 들어갈 객체를 지정하면 this는 해당 객체를 가리키게 된다. 

bind()는 this가 바인딩되는 객체만 변경하고 함수를 실행하지 않는다. 그 대신 변경된 새로운 함수를 반환한다.

 

결과: 

 

원래 this의 주인인 car 객체 값을 출력한다.

 

 


 

ver_6

 <script>
    const car2 = {
      name :"hyundai",
      getName : car.getName,
    };  

    const  bindGetName = car2.getName.bind(car2); 
    bindGetName();
</script>

 

ver_2의 경우에서 bind() 함수만 추가된 것이다. 

즉, bind(car2)가 없다면 Window 내장 객체가 출력되겠지만 bind(car2)로 this를 car2의 값으로 고정시켜 놓은 것이다.

 

const  bindGetName = car2.getName.bind(car2);  

 

결과:

 


 

 

ver_7

<script>
   const testCar = {
      name : "benz",
      getName : function () {
        console.log("getname", this);
        const innerFunc = function() {
          console.log("innerFunc", this);
        };
        innerFunc(); 
      }
    };
    testCar.getName();
</script>

 

결과 값으로 두 번 호출될 값을 예측해 보자 ( 더보기 클릭하면 정답 나옵니다! )

 

 

첫 번째 console.log("getname", this);의 경우 무난하게 맞췄을 것 같다.

 

혹시 두 번째 console.log("innerFunc", this);의 this는 testCar가 호출했기 때문에 첫 번째 결과와 같은 결과를 예상했을 수도 있다. (내가 그랬다.) 

 

여기서 중요하게 볼 점은 innerFunc();를 호출할 때.(닷)이 없다. 

겉보기에는 testCar가 호출하는 것 같지만 ( . ) 이 없기 때문에 Window 객체가 호출한 것이다. 

 

 

그렇다면 innerFunc();를 호출할 때의 this를 첫 번째 this랑 같은 값이 나오게 할 수는 없을까?

 

 

이건 당연히 안된다.

 

 

이것도 안된다.

 

그렇다면 어떻게 해야 할까?

 

 


 

 

ver_8

<script>
const testCar = {
      name : "benz",
      getName : function () {
        console.log("getname", this);
        const innerFunc = () => {
          console.log("innerFunc", this);
        };
        innerFunc();
      }
    };
    testCar.getName();
</script>

 

위처럼 화살표 함수를 사용하게 된다.

화살표 함수에서의 this는 함수가 속해있는 곳의 상위 this를 계승받는다.

 

참고로 화살표 함수는 bind 함수 (바인딩)을 제공하지 않는다. 

 

 

ver_7과 ver_8을 내릴 수 있는 결론은

 

1. this를 쓰고 싶을 때는 일반 함수를 쓰자. 

 

2. 외부 함수 안에 있는 내부 함수 같은 경우 같은 this를 쓴다면 화살표 함수를 사용하자.

 


 

ver_9

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

    ageTest.getAgeList();
</script>

 

내가 원하는 결과 값은 [ 10살, 20살, 30살 ]이지만 실제 출력 값은

 

이렇게 나오게 된다. 

 

this.unit이 Window 내장 객체를 가리키고 있기 때문이다. 

 

해결방법 1

<script>
 const ageTest = {
      unit : "살",
      ageList : [10, 20, 30],
      getAgeList : function() {
        const result = this.ageList.map(function (age) {
          return age + ageTest.bind;
        });
        console.log(result);
      },
    };

    ageTest.getAgeList();
</script>

자기 자신을 호출한다.

 

해결 방법 2

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

 

ref: 

 

 

Comments