티스토리 뷰

01. TL;DR

  • this함수 호출 방식에 따라 동적으로 결정되며, 전역 객체, 호출 객체, 생성 인스턴스 등 다양한 대상을 가리킬 수 있습니다.
  • 함수 내부에서 this정의 시점이 아닌 호출 시점에 따라 바인딩됩니다(렉시컬 스코프와 다릅니다).
  • this 바인딩 문제를 해결하는 방법으로는 클로저(변수 that), bind, apply, call, 화살표 함수(arrow function), 클래스 필드 등이 있습니다.
  • strict mode에서는 일반 함수 호출 시 thisundefined가 됩니다.
  • 화살표 함수는 자신만의 this 바인딩을 갖지 않고, 외부(상위 스코프)의 this를 그대로 사용합니다.

02. this 키워드란?

02.01. 객체와 this의 기본 원리

객체는 상태(프로퍼티)동작(메서드)를 하나의 논리적 단위로 묶는 복합 구조입니다.
자바스크립트 객체의 메서드에서 this해당 메서드를 호출한 객체를 가리키도록 설계되어 있습니다.

02.02. 재귀적 참조와 this

const circle = {
  radius: 5,
  getDiameter() {
    // 자신이 속한 객체를 직접 참조 (비권장)
    return 2 * circle.radius;
  },
};
console.log(circle.getDiameter()); // 10

위 패턴은 자기 자신을 직접 참조하는 방식으로, 변수명 변경이나 재사용 시 불안정할 수 있습니다.
자바스크립트 객체의 메서드는 일반적으로 this를 통해 자기 자신에 접근하는 것이 권장됩니다.

const circle = {
  radius: 5,
  getDiameter() {
    // this로 자기 자신에 접근 (권장)
    return 2 * this.radius;
  },
};
console.log(circle.getDiameter()); // 10

this는 "메서드를 호출한 객체"를 가리키며, 자바스크립트 엔진에 의해 호출 시점에 암묵적으로 바인딩됩니다.

02.03. this 바인딩이란?

바인딩이란 어떤 식별자(이름)와 실제 값을 연결하는 행위입니다.
변수 선언이 변수명과 메모리 공간을 연결하는 것처럼, this 바인딩은 this와 실제 객체를 연결합니다.

function Circle(radius) {
  this.radius = radius;
}
Circle.prototype.getDiameter = function () {
  return 2 * this.radius;
};

const circle = new Circle(5);
console.log(circle.getDiameter()); // 10

생성자 함수 내의 this는 생성될 인스턴스를 가리킵니다.

02.04. 클래스 기반 언어와의 차이

자바/C++ 등 클래스 기반 언어에서는 this가 항상 클래스 인스턴스를 가리킵니다.
반면, 자바스크립트에서는 "함수 호출 방식"에 따라 this가 동적으로 바인딩됩니다.


03. 전역에서의 this, 일반 함수에서의 this

console.log(this); // 브라우저 환경: window, Node.js: global (strict mode에서는 undefined)

// 일반 함수 내의 this (strict mode 미사용)
function foo() {
  console.log(this); // 브라우저: window
}
foo();

strict mode에서 일반 함수의 this는 undefined가 됩니다.

function foo() {
  'use strict';
  console.log(this); // undefined
}
foo();

04. 함수 호출 방식과 this 바인딩

this 바인딩(즉, this가 가리키는 값)은 함수가 어떻게 호출되는지에 따라 동적으로 결정됩니다.

호출 방식 this 값 설명
일반 함수 호출 전역 객체/undefined window/global(strict 모드)
메서드 호출 호출한 객체 메서드 이름 앞의 객체
생성자 함수 호출 생성된 인스턴스 new 키워드로 생성
apply/call/bind 호출 명시적으로 지정한 객체 첫번째 인수(thisArg)로 전달한 값
화살표 함수 상위 스코프의 this 렉시컬 this, 자기 자신만의 this를 갖지 않음
이벤트 핸들러(DOM) 이벤트를 바인딩한 DOM 요소 addEventListener 등
클래스 필드(arrow) 인스턴스 바벨/트랜스파일시에도 주의 필요

04.01. 일반 함수 호출

일반 함수 또는 중첩 함수로 호출될 때 this는 전역 객체(window) 또는 strict 모드에서는 undefined가 됩니다.
중첩 함수, 콜백 함수, setTimeout, Promise 등에서도 함수 내부의 this는 "일반 함수 호출"로 간주합니다.

04.01.01. 예시

function foo() {
  console.log(this); // window (strict 모드: undefined)
  function bar() {
    console.log(this); // window (strict 모드: undefined)
  }
  bar();
}
foo();

04.01.02. 콜백 함수/타이머의 this

const obj = {
  value: 100,
  foo() {
    setTimeout(function () {
      console.log(this); // window/undefined
    }, 100);
  },
};
obj.foo();

04.01.03. 해결책 1: 클로저(변수 that)

const obj = {
  value: 100,
  foo() {
    const that = this;
    setTimeout(function () {
      console.log(that.value); // 100
    }, 100);
  },
};

04.01.04. 해결책 2: bind

const obj = {
  value: 100,
  foo() {
    setTimeout(function () {
      console.log(this.value); // 100
    }.bind(this), 100);
  },
};

04.01.05. 해결책 3: 화살표 함수

const obj = {
  value: 100,
  foo() {
    setTimeout(() => {
      console.log(this.value); // 100
    }, 100);
  },
};

04.02. 메서드 호출

메서드로 호출 시, this는 메서드를 호출한 객체에 바인딩됩니다.
하지만, 메서드를 변수에 할당해서 호출하면 "일반 함수"가 되므로 this는 전역 객체 또는 undefined가 됩니다.

const person = {
  name: 'Lee',
  getName() {
    return this.name;
  },
};
console.log(person.getName()); // Lee

const anotherPerson = { name: 'Kim' };
anotherPerson.getName = person.getName;
console.log(anotherPerson.getName()); // Kim

const getName = person.getName;
console.log(getName()); // window.name 또는 undefined (strict)

04.02.01. 프로토타입 메서드의 this

function Person(name) {
  this.name = name;
}
Person.prototype.getName = function () {
  return this.name;
};

const me = new Person('Lee');
console.log(me.getName()); // Lee

Person.prototype.name = 'Kim';
console.log(Person.prototype.getName()); // Kim

04.03. 생성자 함수 호출

생성자 함수로 호출할 때(new), this는 미래에 생성될 인스턴스에 바인딩됩니다.
new 없이 호출하면 일반 함수와 동일하게 동작합니다.

function Circle(radius) {
  this.radius = radius;
}
const c1 = new Circle(10);
console.log(c1.radius); // 10

const c2 = Circle(20);
console.log(c2); // undefined
console.log(globalThis.radius); // 20

04.04. apply, call, bind를 통한 간접 호출

call, apply는 즉시 호출하며, bind는 새로운 this가 바인딩된 함수를 반환합니다.

function show() {
  console.log(this.value);
}

const obj = { value: 42 };

show();                // undefined (strict) / window.value
show.call(obj);        // 42
show.apply(obj);       // 42
show.bind(obj)();      // 42

04.04.01. arguments 변환 패턴

function test() {
  // arguments는 진짜 배열이 아님
  const args = Array.prototype.slice.call(arguments);
  return args;
}
console.log(test(1, 2, 3)); // [1,2,3]

04.05. 화살표 함수의 this

화살표 함수는 this 바인딩을 생성하지 않으며, 선언 시점의 상위 스코프의 this를 렉시컬하게 가져옵니다.

const obj = {
  value: 1,
  normalFn: function () {
    setTimeout(function () {
      console.log(this.value); // undefined (일반 함수이므로 window/global)
    }, 100);
  },
  arrowFn: function () {
    setTimeout(() => {
      console.log(this.value); // 1 (arrow의 this는 obj를 가리킴)
    }, 100);
  },
};
obj.normalFn();
obj.arrowFn();

화살표 함수의 this는 객체 자체와는 무관하며, 가장 가까운 상위 일반 함수의 this를 그대로 사용함에 유의해야 합니다.


05. 클래스와 this

05.01. 클래스 메서드의 this

클래스 문법에서 메서드는 항상 인스턴스를 가리킵니다.

class Person {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}
const p = new Person('Lee');
console.log(p.getName()); // Lee

05.02. 클래스 필드(arrow)와 this

클래스 필드에서 화살표 함수를 사용하면 this가 항상 인스턴스를 가리킵니다(바벨/트랜스파일러 지원 시).

class Counter {
  count = 0;
  increase = () => {
    this.count++;
    console.log(this.count);
  };
}
const counter = new Counter();
const inc = counter.increase;
inc(); // 1 (arrow function은 this가 인스턴스 고정)

babel 변환 결과

function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
class Counter {
  constructor() {
    _defineProperty(this, "count", 0);
    _defineProperty(this, "increase", () => {
      this.count++;
      console.log(this.count);
    });
  }
}
const counter = new Counter();
const inc = counter.increase;
inc(); // 1 (arrow function은 this가 인스턴스 고정)

06. 이벤트 핸들러에서의 this

06.01. DOM 이벤트 핸들러

브라우저 환경에서 이벤트 핸들러 내 this는 이벤트를 바인딩한 DOM 요소를 가리킵니다.

const button = document.querySelector('button');
button.addEventListener('click', function () {
  console.log(this); // <button> DOM element
});

화살표 함수를 사용하면 상위 스코프의 this(대개 window/global)가 되므로, DOM 요소를 바인딩하고 싶을 때는 function 키워드를 사용해야 합니다.

button.addEventListener('click', () => {
  console.log(this); // window/global
});

07. Proxy, Reflect에서의 this

Proxy 트랩에서의 this는 타겟 객체가 아니라 Proxy 인스턴스를 가리킵니다.
Reflect를 이용하면 타겟의 this를 안전하게 지정할 수 있습니다.

const target = {
  message: "hello",
  getMessage() {
    return this.message;
  }
};
const proxy = new Proxy(target, {
  get(target, prop, receiver) {
    if (typeof target[prop] === 'function') {
      return function (...args) {
        // this는 Proxy를 가리킵니다.
        return target[prop].apply(receiver, args);
      }
    }
    return Reflect.get(target, prop, receiver);
  }
});
console.log(proxy.getMessage()); // hello

08. 실무에서 자주 만나는 this 바인딩 실수 케이스

08.01. 콜백 함수로 객체 메서드 전달

const obj = {
  name: 'Lee',
  getName() { return this.name; }
}
setTimeout(obj.getName, 100); // undefined (일반 함수 호출)
setTimeout(obj.getName.bind(obj), 100); // Lee (바인딩 필요)

08.02. 배열 메서드에서 콜백의 this

const obj = {
  arr: [1,2,3],
  print() {
    this.arr.forEach(function(n) {
      console.log(this, n); // this는 window/global
    });
  }
}
obj.print();

forEach 등에서 두 번째 인수로 thisArg를 줄 수 있습니다.
this.arr.forEach(function(n) { ... }, this);

08.03. 함수의 this와 바벨/트랜스파일 이슈

클래스 필드에서 화살표 함수 사용이 자바스크립트 표준이 아니며, Babel 등이 transform하여 동작시킵니다.
전통적 클래스에서는 반드시 메서드 내부에서 this 사용에 주의해야 합니다.


09. this 바인딩에 대한 심층 FAQ

  • this는 언제 결정되는가?
    this는 함수가 호출되는 시점에 결정됩니다(정의 시점이 아닙니다).
  • arrow function은 항상 안전한가?
    아니오, 의도치 않게 상위 스코프의 this를 따라가기 때문에 실수할 수 있습니다.
  • strict mode에서 this는?
    일반 함수 호출 시 undefined가 됩니다.
  • apply, call, bind 차이?
    • call: fn.call(thisArg, ...args)로 즉시 호출합니다.
    • apply: fn.apply(thisArg, [args])로 즉시 호출합니다(배열 인자).
    • bind: fn.bind(thisArg, ...args)로 새 함수를 반환합니다.
  • 메서드 분리/대입 시 this는 어떻게 되나요?
    변수를 옮겨 호출하거나 콜백 전달 등에서 일반 함수로 동작합니다.

10. 결론

  • 자바스크립트의 this는 호출 시점호출 방법에 따라 달라집니다.
  • 실무에서는 의도치 않은 this 바인딩 문제가 자주 발생합니다.
  • 항상 "this가 무엇을 가리키는가?"를 디버깅하며 코드를 작성해야 합니다.
  • 최신 문법(arrow, class fields, bind 등)과 구문 호환성에도 주의가 필요합니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30
글 보관함