이번에 this 바인딩 규칙을 정리하면서, 단순히 ‘전역에서는 window', '메서드에서는 객체’ 정도로만 이해했던 개념이 훨씬 복잡하고 상황에 따라 다르게 동작한다는 것을 깨달았습니다. 특히 함수 참조 분리 시 this 소실, ES 모듈에서의 this = undefined, 그리고 이벤트 핸들러에서의 this 차이(일반 함수 vs 화살표 함수) 같은 부분은 실제 코드에서 자주 마주칠 수 있는 상황이라고 느껴 실제로 개발하면서도 적절하게 사용해보려고 합니다.
JavaScript에서 this는 함수가 호출되는 방식에 따라 동적으로 결정되는 실행 컨텍스트의 참조 객체입니다. 따라서 함수가 어디서 정의되었는지가 아니라 어떻게 호출되었는지에 따라 값이 달라집니다.
- 전역에서는 전역 객체(window / global)
- 객체의 메서드 호출 시에는 그 객체
- 생성자 호출 시에는 새로 만들어진 인스턴스
- 화살표 함수에서는 상위 스코프의 this
이처럼 상황마다 값이 바뀌는 것이 JavaScript this의 가장 큰 특징입니다.
특징
JavaScript의 this는 함수 또는 메서드 내에서 동작하며, 호출되는 방식에 따라 그 값이 결정되는 특성을 가집니다.
this의 값은 함수가 호출되는 시점에 결정되며, JavaScript는 함수와 메서드의 구분이 느슨하여 동일한 함수라도 호출 방식에 따라 this의 결과가 달라질 수 있습니다.
this 바인딩 규칙
1) 전역 컨텍스트
전역 공간에서의 this는 실행 환경에 따라 다르게 적용됩니다.
- 브라우저에서는 window, Node.js에서는 global
- ES 모듈 최상단에서는 엄격 모드가 기본 적용 → undefined
console.log(this); // 브라우저: window, Node.js: global
"use strict";
function foo() {
console.log(this); // undefined
}
foo();
2) 함수로서 호출
함수를 독립적으로 호출하면 this는 전역 객체를 가리키며, strict mode에서는 undefined가 됩니다.
function func() {
console.log(this);
}
func(); // window 또는 undefined
3) 메서드로서 호출
메서드 내부의 this는 해당 메서드를 호출하는 시점에서 가장 가까운 객체를 가리킵니다.
특정 객체와 연계되어 동작할 때 this는 호출한 객체가 됩니다.
var obj = {
value: 10,
getValue: function() {
console.log(this.value);
}
};
obj.getValue(); // 10
4) 메서드 내부 함수
메서드 내부에서 선언된 일반 함수는 독립적으로 호출되므로 this는 전역 객체를 가리킵니다. 이를 해결하려면 bind를 사용하거나 화살표 함수를 사용할 수 있습니다.
var obj = {
outer: function() {
function inner() {
console.log(this);
}
inner(); // window 또는 undefined
}
};
obj.outer();
5) 생성자 호출
생성자 함수에서 this는 new 키워드와 함께 호출될 때 새로 생성된 인스턴스를 가리킵니다. 이 과정에서 this에 바인딩된 속성과 메서드가 인스턴스에 할당됩니다.
function Person(name) {
this.name = name;
}
const p1 = new Person("Daeun");
console.log(p1.name); // "Daeun"
6) 화살표 함수
화살표 함수는 자신만의 this를 가지지 않고, 선언될 당시 상위 스코프의 this를 그대로 사용합니다. 따라서 call이나 bind로 this를 바꿀 수 없습니다.
const obj = {
value: 42,
normal: function() {
const arrow = () => console.log(this.value);
arrow();
}
};
obj.normal(); // 42
7) 명시적 바인딩
JavaScript는 call, apply, bind 메서드를 사용해 this를 명시적으로 바인딩할 수 있습니다. call과 apply는 함수를 즉시 실행시키면서 this를 바꾸고, bind는 새로운 함수를 반환하여 this를 영구적으로 묶습니다.
function greet(msg) {
console.log(msg, this.name);
}
greet.call({ name: "Daeun" }, "Hi"); // Hi Daeun
greet.apply({ name: "Daeun" }, ["Hey"]); // Hey Daeun
const bound = greet.bind({ name: "Daeun" });
bound("Hello"); // Hello Daeun
8) 콜백 함수에서의 this
배열 메서드의 콜백 함수는 기본적으로 전역 객체를 가리키지만, 두 번째 인자로 this를 직접 지정할 수 있습니다. 또는 bind를 사용하여 this를 고정할 수도 있습니다.
const obj = {
count: 0,
increase() {
[1, 2, 3].forEach(function() {
this.count++;
}, this);
}
};
obj.increase();
console.log(obj.count); // 3
this 바인딩 문제를 관리하고 명시적으로 제어하는 방법
1) call() 메서드
Function.prototype.call(thisArg[, arg1[, arg2[...]]]) 형태로 사용되며, 첫 번째 매개변수로 전달된 thisArg를 this에 바인딩함과 동시에 함수를 즉시 실행시킵니다.
2) apply() 메서드
Function.prototype.apply(thisArg[, argsArray]) 형태로 사용되며, call() 메서드와 동일하게 thisArg를 this에 바인딩하고 함수를 즉시 실행시킵니다. 차이점은 함수 자체의 매개변수를 Array 형태로 입력받는다는 점입니다.
3) bind() 메서드
Function.prototype.bind(thisArg[, arg1[, arg2[...]]]) 형태로 사용되며, 함수의 this를 thisArg로 영구적으로 변경하고, 함수를 즉시 실행하지 않고 새로운 함수를 반환합니다.
4) 콜백 함수에서의 this 바인딩
ES6에서 추가된 배열 메서드(Array.map, filter, reduce)는 콜백 함수 내부에서 this를 어떤 것으로 할지를 두 번째 매개변수로 받을 수 있습니다. bind() 함수를 사용해서도 콜백 함수의 this를 바인딩할 수 있습니다.
실제 사용 예시
1) React 클래스 컴포넌트에서의 this 바인딩
클래스 컴포넌트 메서드는 자동으로 this가 바인딩되지 않습니다. 이벤트 핸들러에서 this를 사용하려면 생성자에서 bind를 하거나, 화살표 함수로 정의해야 합니다.
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleClick = this.handleClick.bind(this); // 바인딩 필수
}
handleClick() {
this.setState({ count: this.state.count + 1 });
}
render() {
return <button onClick={this.handleClick}>+</button>;
}
}
2) 브라우저 이벤트 핸들러
DOM 이벤트 리스너에서 일반 함수는 this가 이벤트 타깃을 가리키지만, 화살표 함수는 상위 스코프의 this를 따릅니다.
const btn = document.querySelector("button");
btn.addEventListener("click", function() {
console.log(this); // <button> 엘리먼트
});
btn.addEventListener("click", () => {
console.log(this); // window (상위 스코프의 this)
});
3) 콜백 함수 내부의 this (배열 메서드)
forEach 같은 고차 함수에서 this를 명시적으로 지정하지 않으면 전역 객체를 가리키게 됩니다. 두 번째 인자나 bind로 제어할 수 있습니다.
const counter = {
num: 0,
inc() {
[1, 2, 3].forEach(function() {
this.num++;
}, this); // 두 번째 인자로 this 전달
}
};
counter.inc();
console.log(counter.num); // 3
보조 개념 정리
- 엄격 모드 (Strict Mode): "use strict" 선언 시 일반 함수 호출에서 this가 전역 객체가 아닌 undefined로 바인딩
- 바인딩 소실 (Lost Binding): 객체의 메서드를 변수에 할당 후 독립 실행하면 this 연결이 끊어져 전역 객체(또는 undefined)를 가리키는 현상
- 암묵적 바인딩 (Implicit Binding): 메서드를 객체를 통해 호출할 때 자동으로 this가 그 객체에 바인딩되는 규칙
- 명시적 바인딩 (Explicit Binding): call, apply, bind와 같은 내장 메서드를 사용하여 this를 강제로 지정하는 방식
'📚 cs' 카테고리의 다른 글
[JavaScript] 스코프 체인 (Scope Chain) (1) | 2025.08.31 |
---|---|
[JavaScript] V8 엔진과 GC (Garbage Collection) (3) | 2025.08.28 |
[JavaScript] 실행 컨텍스트와 호이스팅 (1) | 2025.08.27 |
개발자 기초 cs - API와 아키텍처 (1) | 2025.08.26 |
개발자 기초 cs - 컴퓨터 네트워크 기초 (2) | 2025.08.25 |