frontend/javascript

🟨 2-17. this 완전 정복 — 함수, 객체, 클래스에서의 this 바인딩 규칙과 혼동 포인트

mirabo01 2025. 11. 7. 08:53

1. this란 무엇인가

this는 현재 실행 중인 컨텍스트(Context) 를 참조하는 특별한 키워드다.
즉, 코드가 “누구의 입장에서 실행되었는가” 에 따라 그 대상이 달라진다.

this는 정의될 때가 아니라, 실행될 때 결정된다.

이게 핵심이야.


2. 전역 컨텍스트에서의 this

브라우저 환경에서는 전역 this가 window 객체를 가리킨다.

console.log(this === window); // true

Node.js에서는 global 또는 module.exports를 가리킨다.

즉, 전역 this는 환경에 따라 다르다.


3. 함수에서의 this (기본 바인딩 규칙)

function showThis() {
  console.log(this);
}
showThis(); // window (브라우저) / undefined (strict mode)

일반 함수 호출에서는 this가 전역 객체를 가리킨다.
✅ 단, 'use strict' 모드에서는 undefined.


4. 객체 메서드에서의 this

const user = {
  name: "기범",
  sayHello() {
    console.log("안녕, 나는", this.name);
  },
};
user.sayHello(); // 안녕, 나는 기범

✅ user.sayHello()는 “user 객체의 맥락”에서 실행됨.
✅ 즉, this는 user 객체를 참조한다.

💡 하지만 객체의 메서드를 변수로 분리하면 this가 달라진다.

const greet = user.sayHello;
greet(); // undefined 또는 'window' (this가 사라짐)

이런 현상이 발생하는 이유는,
메서드가 객체에서 떨어져 나가면 **“누가 호출했는가”**의 정보가 사라지기 때문이다.


5. 생성자 함수에서의 this

new 키워드로 함수를 호출하면,
this는 새로 생성되는 인스턴스를 가리킨다.

function User(name) {
  this.name = name;
}
const kb = new User("기범");
console.log(kb.name); // 기범

✅ new를 사용하면 내부적으로 다음이 일어난다:

  1. 새로운 객체 {} 생성
  2. 그 객체를 this에 바인딩
  3. 함수 실행
  4. 객체 반환

6. 명시적 바인딩 — call, apply, bind

this를 수동으로 바꿀 수도 있다.

🔹 call

function greet() {
  console.log("안녕, 나는 " + this.name);
}
const user = { name: "기범" };
greet.call(user); // 안녕, 나는 기범

🔹 apply

function introduce(lang1, lang2) {
  console.log(`${this.name}는 ${lang1}와 ${lang2}를 사용합니다.`);
}
const dev = { name: "기범" };
introduce.apply(dev, ["JavaScript", "Python"]);

🔹 bind

bind()는 this를 고정한 새 함수를 반환한다.

const bound = greet.bind(user);
bound(); // 안녕, 나는 기범

✅ call과 apply는 즉시 실행
✅ bind는 “묶은 함수”를 나중에 실행


7. 화살표 함수(Arrow Function)에서의 this

화살표 함수는 this를 자체적으로 바인딩하지 않는다.
즉, 상위 스코프의 this를 그대로 상속받는다.

const user = {
  name: "기범",
  normal() {
    console.log(this.name);
  },
  arrow: () => {
    console.log(this.name);
  },
};
user.normal(); // 기범
user.arrow();  // undefined (상위 스코프: window)

✅ 화살표 함수는 this가 “고정(bound)”되어 있다.
✅ 따라서 메서드 정의에는 화살표 함수 사용을 피해야 한다.


8. 콜백 함수 안에서의 this 문제

비동기 콜백에서는 this가 종종 의도치 않게 사라진다.

const counter = {
  count: 0,
  start() {
    setTimeout(function () {
      console.log(this.count);
    }, 1000);
  },
};
counter.start(); // undefined

왜냐면 setTimeout 내부의 콜백은 일반 함수 호출로 간주되기 때문이야.
해결 방법은 2가지 👇

✅ 1) 화살표 함수 사용

setTimeout(() => console.log(this.count), 1000);

✅ 2) 변수에 this 저장

const self = this;
setTimeout(function () {
  console.log(self.count);
}, 1000);

9. 클래스에서의 this

클래스 내부에서는 this가 해당 인스턴스를 가리킨다.

class User {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    console.log(`안녕, 나는 ${this.name}`);
  }
}

const kb = new User("기범");
kb.sayHi(); // 안녕, 나는 기범

하지만 클래스에서도 this가 유실되는 상황이 있다 👇

const say = kb.sayHi;
say(); // ❌ this undefined

해결 방법은 bind로 메서드를 고정하는 것이다.

this.sayHi = this.sayHi.bind(this);

또는 화살표 메서드를 사용해 상위 스코프의 this를 유지할 수도 있다.

class User {
  constructor(name) {
    this.name = name;
  }
  sayHi = () => console.log(`안녕, 나는 ${this.name}`);
}

10. 브라우저 이벤트 리스너에서의 this

const btn = document.querySelector("button");
btn.addEventListener("click", function () {
  console.log(this); // <button> 요소
});

✅ 일반 함수에서는 this가 “이벤트 대상”을 가리킴.
✅ 화살표 함수에서는 상위 스코프의 this를 사용하므로 버튼이 아님.

btn.addEventListener("click", () => {
  console.log(this); // window
});

11. this 바인딩 규칙 요약표

호출 방식 this가 가리키는 대상

전역 window / global
일반 함수 window (strict 모드에서는 undefined)
객체 메서드 호출한 객체
생성자 함수 새로 생성된 인스턴스
call / apply / bind 명시적으로 지정된 객체
화살표 함수 상위 스코프의 this
클래스 메서드 인스턴스
이벤트 핸들러 (function) 이벤트 발생한 요소
이벤트 핸들러 (arrow) 상위 스코프

12. 실무 예제 — React에서의 this

React의 클래스형 컴포넌트는 this를 정확히 이해해야 동작한다.

class App extends React.Component {
  constructor() {
    super();
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return <button onClick={this.handleClick}>{this.state.count}</button>;
  }
}

✅ bind(this)를 하지 않으면 this가 undefined가 되어 setState 호출 불가
✅ 함수형 컴포넌트의 useState와 useEffect는 이 문제를 근본적으로 제거함


13. 마무리

이제 자바스크립트의 this가
**“정의 시점이 아니라 실행 시점에 결정된다”**는 걸 명확히 이해했을 거야.
모든 헷갈림은 결국 “누가 함수를 호출했는가”를 생각하면 풀린다.

“this를 이해하지 못하면 자바스크립트는 마법처럼 보이지만,
이해하면 논리적인 언어가 된다.”