Skip to content

this

자바스크립트에서 this가 가리키는 대상은 함수 호출 방식에 따라 결정된다. 이러한 호출 방식에 의해 결정되는 환경을 실행 문맥(execution context)이라고 하며, this는 그 문맥에 따라 어떤 객체에 바인딩되어 참조 대상이 동적으로 결정된다. 즉, this는 실행되는 시점과 방식에 따라 연결되는 객체(바인딩 대상)가 달라지는 키워드다.

less
this
└── 함수 호출 방식
    ├── 일반 함수 호출 -> 전역 객체
    ├── 메서드 호출 -> 메서드를 포함하고 있는 객체
    ├── 생성자 함수 호출 -> 생성할 객체
    └── 콜백 함수 호출 -> 전역 객체 (일반 함수처럼 호출될 경우)

전역 컨텍스트(Global Context)

전역 코드에서 this는 실행 환경에 따라 전역 객체를 가리킨다.

js
console.log(this); // 브라우저에서는 window
  • 브라우저 환경: window 객체 참조
    window는 브라우저의 전역 객체로, 전역 변수나 함수, 타이머 함수(setTimeout 등), 브라우저 API 등을 포함한다.
  • Node.js 환경: global 객체 참조
    global은 Node.js의 전역 객체로, 브라우저의 window와 유사하게 전역 변수, 함수, 모듈, 타이머 등을 포함한다.

함수 호출 방식

일반 함수 호출

js
function func() {
  console.log(this);
}
func(); // 브라우저에서는 window
  • 일반 함수가 호출될 때, 함수 내부의 this는 전역 객체(window)를 참조한다.
  • 이는 호출한 주체가 없기 때문이며, this는 기본적으로 전역 객체에 바인딩된다.
  • Strict mode에서는 this가 undefined가 되며, 암묵적으로 전역 객체에 바인딩되지 않는다.

메서드 호출

js
const cafe = {
  brand: "starbucks",
  menu: "americano",
  print: function () {
    console.log(this);
  },
};

cafe.print();
// { brand: "starbucks", menu: "americano", print: function () { ... }}
  • 메서드란, 객체의 프로퍼티로 저장된 함수를 말한다.
  • 메서드 내부에서 this는 해당 메서드를 호출한 객체 자신을 가리킨다.
  • 위 예제에서는 cafe.print()가 호출되었기 때문에, this는 cafe 객체를 참조한다. 즉, this === cafe이다.
js
const cafe = {
  brand: "starbucks",
  menu: "americano",
  newCafe: {
    brand: "starbucks",
    menu: "latte",
    print: function () {
      console.log(this);
    },
  },
};

cafe.newCafe.print();
// { brand: "starbucks", menu: "latte", print: function () { ... }}
  • print 함수는 newCafe 객체의 메서드로 호출되므로, this는 newCafe 객체를 가리키게 된다.
  • cafe.newCafe.print()에서 print는 newCafe 객체에 속해 있으므로, this === cafe.newCafe가 된다.
js
const cafe = {
  brand: "starbucks",
  menu: "americano",
  print: function () {
    console.log(this);
  },
};

const myCafe = cafe.print;
myCafe(); // window
  • 함수가 객체의 프로퍼티로 존재해도, 호출 방식이 일반 함수라면 this는 해당 객체를 참조하지 않는다.
  • this는 함수를 어떻게 호출했는지에 따라 결정되는 키워드다. 위 코드에서 print 함수는 cafe 객체의 메서드지만, myCafe에 할당된 뒤 일반 함수로 호출되었기 때문에 this는 전역 객체(window)를 가리킨다.
  • 함수가 객체의 메서드로 호출되지 않으면, this는 해당 객체를 잃고 전역 컨텍스트에서 동작한다.

생성자 함수 호출

js
function Cafe(menu) {
  console.log(this); // new에 의해 만들어진 빈 객체: Cafe { }
  this.menu = menu;
}

let myCafe = new Cafe("latte");

console.log(myCafe); // Cafe { menu: "latte" }

// Cafe { }
// Cafe { menu: "latte" }
  • 생성자 함수는 new 키워드와 함께 호출되어 새로운 객체(인스턴스)를 생성한다.
  • 이때 this는 새로 생성 중인 객체를 가리키며, 생성자 함수 내부에서 해당 객체에 프로퍼티를 추가할 수 있다.
  • 생성자 내부의 this는 빈 객체로 시작해, 프로퍼티를 추가하고 그 객체를 반환하게 된다.
💡 new와 생성자 함수가 함께 작동하는 방식

🔍 생성자 함수란?

js
// Cafe 함수 정의
function Cafe(menu) {
  console.log(this); // Cafe { }
  this.menu = menu;
}

생성자 함수로 사용하기 위해 함수 이름은 일반적으로 대문자로 시작하는 관례를 따른다.


js
let myCafe = new Cafe("latte");

Cafe 함수를 생성자 함수로 호출하면서, 새로운 객체(Cafe의 인스턴스)를 만들어 myCafe에 할당한다.
내부적으로는:

  1. 빈 객체가 생성된다: this = {}
  2. Cafe 함수가 실행되며 this.menu = menuthis.menu = "latte"가 실행된다.
  3. 마지막에 this가 자동으로 반환되어 myCafe에 할당된다.
js
// new 없이 호출
function Cafe(menu) {
  console.log(this);
  this.menu = menu;
}

let myCafe = Cafe("latte");

console.log(myCafe);

// window
// undefined
  • new 없이 함수를 호출하면 일반 함수로 실행되며, 내부의 this는 전역 객체(window)를 가리킨다.
  • 이 경우 Cafe 함수는 객체를 반환하지 않기 때문에(return문이 존재하지 않음) myCafe에는 undefined가 할당된다. 따라서 생성자 함수는 반드시 new와 함께 사용해야 의미가 있다.

콜백 함수 호출

js
const cafe = {
  brand: "starbucks",
  menu: "",
  setMenu: function (menu) {
    this.menu = menu;
  },
};

function getMenu(menu, callback) {
  callback(menu); // 일반 함수처럼 호출
}

getMenu("핫초코", cafe.setMenu);

console.log(cafe);
// { brand: "starbucks", menu: "", setMenu: [Function: setMenu] }
  • 함수가 콜백 함수로 전달되어 일반 함수처럼 호출되면, 해당 함수의 this는 전역 객체(window 또는 global)를 가리킨다.
  • getMenu("핫초코", cafe.setMenu)에서 cafe.setMenu는 함수 자체(참조)를 참조 형태로 전달한 것이다. 이때 함수는 cafe 객체의 메서드로 묶인 상태가 아니며, 단순한 일반 함수처럼 callback(menu)으로 호출된다.
  • 그래서, setMenu 내부의 this는 더 이상 cafe를 가리키지 않고, 전역 객체(window 또는 global)를 가리킨다.
  • 결국 this.menu = menu;는 cafe 객체의 프로퍼티를 바꾸지 못하고, 전역 객체에 menu를 추가하려고 시도한다.