처음 회사에 갔을때 this에 대해 잘 몰라서 로직을 파악하기 너무 힘들었던 기억이 난다.. 그 이후 this 에 대해서 찾아보기도 많이 찾아보고 잘 알고 있다고 생각했지만 설명하라고 해보면 하기가 막막한것 같아서 this에 관해 확실히 정리하고 넘어가면 좋을것 같아서 이 글을 작성한다.

 

자바스크립트의 this 란?

생각해보면 우리 모두 자기 자신을 "나"라고 부른다. 자바스크립트도 비슷하다. 함수나 객체 안에서 this는 "내가 누구인가?"를 알려주는 역할을 한다.

 

즉 "내 자신" 또는 "내가 속한 주체"를 가리키는 특별한 키워드이다.

 

this를 왜 사용할까? 

 

코드를 작성할 때 객체의 내부 함수에서 매번 객체의 이름을 쓰는 대신 this를 사용하면 코드가 더 깔끔해지고, 객체의 이름이 바뀌어도 수정이 편해진다.

 

이해가 잘 안간다.. 그렇다면 직접 해보는게 가장 좋다.  this 없이 객체의 속성에 접근해보자

const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + person.name + " 이야!");
  }
};

person.greet();
// 실행 결과: 안녕, 나는 Bob 이야!

 

여기서 문제점이 발생한다. 만약 person의 이름이 바뀐다면? 직접 함수 내부의 person.name 의 값을 변경해줘야 한다.

 

반면 this를 사용한 경우는

const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + this.name + " 이야!");
  }
};

person.greet();
// 실행 결과: 안녕, 나는 Bob 이야!

 

객체의 이름을 변경해도 내부 코드를 수정할 필요가 없다. 왜냐하면 this는 항상 현재 객체를 가리키기 때문이다.

 

이해가 되는가? 설명이 좀 부족해보인다. 다른 예시를 들어보자,

this없이 객체 속성에 접근한 예시를 하나 더 살펴보자

const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + person.name + " 이야!");
  }
};

const anotherPerson = {
  name: "Alice",
  // person 객체의 greet 함수를 재사용
  greet: person.greet
};

person.greet();
// 실행 결과: 안녕, 나는 Bob 이야!

anotherPerson.greet();
// 실행 결과: 안녕, 나는 Bob 이야! -> 기대했던 "Alice"가 아니라 "Bob"이 출력됨

 

이제야 이해가 된다! 위 예시를 보니 this가 정말 편리하다고 느껴진다.

 

1. 객체 안에서의 this

1-1 메서드 호출과 this

객체 내부의 함수(메서드를 호출할 때, this는 자동으로 그 객체를 가리킨다. 즉, 객체이름.메서드() 형태로 호출하면, 메서드 내부의 this는 객체이름이 된다.

 

const student = {
  school: "초등학교",
  study: function() {
    console.log("나는 " + this.school + "에서 공부해!");
  }
};

student.study();
// 실행 결과: 나는 초등학교에서 공부해!

 

 

this.school은 student 객체 안의 school 속성을 가져온다.

 

 

1-2 메서드 내부에서의 this

메서드 내부에 this를 사용하면 어떨까?

const user = {
	name : "Alice",
    greet: function() {
    	console.log(this);
    }
};


user.greet();
// 출력 : {name : '홍길동', greet : [Function: greet] }

 

위의 코드에서 this 는 greet 메서드를 호출한 객체인 user를 가리킨다. 자바스크립트는 메서드가 호출될 때, 호출한 객체를 자동으로 this에 바인딩한다. 

 

메서드 호출 방식이 this의 동작을 결정짓는 키이다.

 

 

 

더보기
더보기

1-3 중첩 함수에서의 this 문제

 

객체의 메서드 안에 또 다른 함수를 만들면, 이 내부 함수에서는 this가 예상과 다르게 동작할 수 있다.

 

중첩 함수와 this 예제를 보자

const teacher = {
  subject: "수학",
  explain: function() {
    function inner() {
      console.log("나는 " + this.subject + "를 가르쳐!");
    }
    inner();
  }
};

teacher.explain();
// 실행 결과: 브라우저에서는 undefined 혹은 전역 객체의 subject (대부분 undefined)

 

해결방법은

  1. 외부 변수에 this 저장하기(self, that 사용)
  2. 화살표 함수 사용하기

가 있다.

 

 

self를 사용하기

const chef = {
  dish: "파스타",
  cook: function() {
    const self = this;  // this를 self라는 변수에 저장
    function innerCook() {
      console.log("오늘의 요리는 " + self.dish + "입니다!");
    }
    innerCook();
  }
};

chef.cook();
// 실행 결과: 오늘의 요리는 파스타입니다!

 

self 변수에 this를 저장하여 내부 함수에서도 외부의 this를 참조할 수 있게 한다.

 

2. 일반 함수에서의 this

 

2-1 함수 호출 방식에 따른 this 변화

함수를 그냥 따로 실행할 때도 있는데, 그 경우 this가 가리키는 대상이 달라질 수 있다. 

 

전역 함수에서의 this를 살펴보자

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

showThis();
// 실행 결과 (브라우저의 경우): Window 객체(전역 객체)가 출력됨

 

이렇게 호출하면 this는 브라우저에서는 window 객체를, Node.js에서는 전역 객체를 가리킨다.

 

 

 

3. 화살표 함수와 this

화살표 함수란 무엇인가? 

화살표 함수 (Arrow Function)는 짧게 함수를 작성할 수 있게 도와주고, 특별한 점은 자신만의 this를 자지지 않는다는 것이다.

 

엥? 이건 또 무슨 말인가..

 

화살표 함수 내부의 this는 바깥 함수의 this를 그대로 사용한다.

 

예제를 봐보자

const person = {
  name: "Alice",
  sayName: function() {
    const innerFunc = () => {
      console.log("내 이름은 " + this.name);
    }
    innerFunc();
  }
};

person.sayName();
// 실행 결과: 내 이름은 Alice

 

innerFunc 는 화살표 함수이기 때문에 자신의 this를 생성하지 않고, 바로 sayName 함수의 this, 즉 person 객체를 그대로 사용한다.

 

 

화살표 함수의 장단점

 

장점 : 

  • 코드가 간결해지고,  중첩 함수에서도 외부 this를 그대로 사용할 수 있다.

단점 : 

  • 자체 this가 없기 때문에, 특정 상황에서는 의도한 대로 동작하지 않을 수 있다. 
const animal = {
  type: "고양이",
  speak: () => {
    console.log("나는 " + this.type);
  }
};

animal.speak();
// 실행 결과: 나는 undefined (혹은 전역 객체의 type 값)
// 왜냐하면 화살표 함수는 자신의 this가 없으므로 전역 this를 사용하기 때문임

 

위의 예시를 통해 객체의 메서드를 정의할 때는 일반 함수를 사용하는 것이 좋다는 점을 알 수 있다.

 

 

 

4. 콜백 함수에서의 this

일반 함수와 마찬가지로, 콜백 함수 내부의 this는 함수가 어떻게 호출되는지에 따라 달라진다. 

문제는, 콜백 함수로 전달된 함수는 호출될 때 원래의 객체 컨텍스트와는 별개로 호출되는 경우가 많다는 것이다.

 

 const person = {
  name: "Bob",
  greet: function() {
    console.log("안녕, 나는 " + this.name + " 이야!");  // 올바른 출력: "안녕, 나는 Bob 이야!"
    
    // setTimeout에 일반 함수 콜백 전달
    setTimeout(function() {
      // 이 내부의 this는 전역 객체(window) 또는 undefined(엄격 모드)로 설정됨
      console.log("안녕, 나는 " + this.name + " 이야! (setTimeout 내부)");
    }, 1000);
  }
};

person.greet();
// 실행 결과:
// 즉시 출력: 안녕, 나는 Bob 이야!
// 1초 후 출력 (브라우저 일반 모드): 안녕, 나는 undefined 이야!

 

위 코드가 어떻게 동작하는지 잠시 생각해보자.

 

왜 저런 결과가 나오는 것일까?

  • person.greet() 에서의 this는 person 객체를 가리킨다
  • 하지만, setTimeout에 전달된 일반 함수 콜백은 전역 함수처럼 호출되기 때문에, 그 내부의 this는 전역 객체(브라우저에서는 window)를 가리킨다.
  • 만약 전역 객체에 name이라는 속성이 없다면 undefined가 출력되는 것이다

 

4-1 이벤트 핸들러에서의 this

웹 페이지의 이벤트 핸들러에서도 콜백 함수의 this 동작은 중요하다. 이벤트 핸들러에서의 this는 주로 이벤트를 발생시킨 DOM 요소를 가리킨다.

 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>이벤트 핸들러에서의 this 예제</title>
  </head>
  <body>
    <button id="myButton">클릭해봐!</button>
    <script>
      const button = document.getElementById("myButton");
      
      // 일반 함수 콜백의 경우, this는 버튼 요소를 가리킴
      button.addEventListener("click", function() {
        console.log("버튼 텍스트 (일반 함수): " + this.innerText);
      });
      
      // 화살표 함수 콜백의 경우, this는 상위 스코프의 this를 상속받음 (이 경우 window)
      button.addEventListener("click", () => {
        console.log("버튼 텍스트 (화살표 함수): " + this.innerText);
      });
    </script>
  </body>
</html>

 

일반 함수 콜백에서는 버튼의 텍스트가 정상적으로 출력되는걸 볼 수 있다. 하지만 화살표 함수 콜백에서는 this가 전역 객체를 참조하기 때문에, this.innerText가 기대한 값이 나오지 않는다.

 

즉 일반 함수 콜백으로 사용시 이벤트 핸들러로 등록된 일반 함수(즉, function 키워드로 작성된 함수)는 호출될 때 this가 해당 이벤트를 발생시킨 요소(예: 버튼, 링크 등)를 가리킨다.

 

 <button id="myButton">클릭해봐!</button>
<script>
  const button = document.getElementById("myButton");
  button.addEventListener("click", function() {
    // 이 내부에서의 this는 클릭한 버튼 요소 자체를 가리킴
    console.log("버튼의 텍스트는: " + this.innerText);
  });
</script>

 

버튼을 클릭하면 콘솔에 "버튼의 텍스트는: 클릭해봐!" 가 출력된다.

 

 

'Front-End > JavaScript' 카테고리의 다른 글

원시 값과 객체의 비교  (1) 2024.07.24
함수(2)  (0) 2024.07.21
함수(1)  (1) 2024.06.18
함수 표현식  (0) 2024.06.17
스코프  (3) 2024.06.14

+ Recent posts