자바스크립트 this
자바스크립트에서 this 키워드는 함수가 호출될 때 해당 함수의 실행 컨텍스트를 나타내는 특별한 키워드이다.
this는 함수가 어떻게 호출되었는지에 따라 다른 값을 가지며, 자바스크립트 코드의 동작을 이해하는 데 매우 중요한 개념이다.
this의 다양한 상황별 값
- 전역 컨텍스트 (Global Context):
- 브라우저 환경에서는 window 객체를 가리킨다.
- Node.js 환경에서는 global 객체(전역 객체)를 가리킨다.
- 엄격 모드 ("use strict")에서는 undefined를 가리킨다.
- 함수 컨텍스트 (Function Context)
- 일반 함수 호출 시
- 엄격 모드에서는 undefined를 가리킨다.
- 비엄격 모드에서는 전역 객체를 가리킨다.
- 메서드 호출 시:
- 메서드를 호출한 객체를 가리킨다.
- 생성자 함수 호출 시:
- 새로 생성된 인스턴스 객체를 가리킨다.
- 화살표 함수:
- 화살표 함수는 자신만의 this 컨텍스트를 가지지 않으며, 상위 스코프의 this 값을 상속받는다.
- 일반 함수 호출 시
- 이벤트 핸들러 컨텍스트 (Event Handler Context):
- 이벤트 핸들러 함수 내에서 this는 이벤트를 발생시킨 DOM 요소를 가리킨다.
- call, apply, bind 메서드:
- call, apply, bind 메서드를 사용하여 this 값을 명시적으로 지정할 수 있다.
console.log(this); // 브라우저 환경: window, Node.js 환경: global (전역 객체)
2. 일반적인 함수에서의 "this"
function myFunction() {
console.log(this); // 일반 함수 호출 (엄격 모드: undefined, 비엄격 모드: 전역 객체)
}
myFunction();
3. 메서드에서의 "this"
const myObject = {
myMethod: function() {
console.log(this); // 메서드 호출 (myObject) - 해당 메서드를 호출한 객체
}
};
myObject.myMethod();
어째서 myObject가 아니라 object를 반환하나요?
여기서 this가 object(객체)를 반영하는 것은 const myObject가 객체(object)가 아니라 변수이기 때문이다. 변수의 이름이 마치 객체 같아서 착각하게 되는 것이다.
const myObject는 객체가 아니라 변수이며, 객체를 직접 저장하는 것이 아니라, 객체가 위치한 메모리를 참조한다.
다르게 말하자면, this는 실행 컨텍스트에 따라 특정 객체를 참조하는 값이지, 단순히 객체의 메모리 주소를 나타내는 변수를 뜻하는 것이 아니다.
자바스크립트 엔진이 객체를 생성할 때, 객체는 힙(heap) 메모리에 저장되며, 해당 객체의 속성 이름, 속성 값, 메서드, 타입 정보 등이 포함된다. 하지만 여기에는 객체의 이름이 지정되지 않는다.
우리가 흔히 객체에 이름이 있다고 착각할 수 있는 이유는 변수에는 이름이 있기 때문이다. 하지만 변수는 데이터가 저장된 공간의 주소만을 참조할 뿐, 객체 그 자체가 아니다.
이전 메모리 관련 글에서 자바스크립트의 메모리 저장 방식을 설명한 적이 있었는데, 객체는 힙(heap) 메모리에 저장되며, 변수는 해당 객체의 참조값(메모리 주소)을 저장한다. 콜스택(Stack)에는 변수와 해당 참조값이 저장되고, 변수 내부에는 객체의 참조값이 들어 있다.
하지만 힙에 저장된 객체 자체에는 이름이 따로 저장되지 않기 때문에 모든 객체는 이름이 없다.
단, 이것에서 예외처럼 보이는 것이 함수인데, 함수 객체에는 자바스크립트 엔진이 자동으로 할당하는 name 속성이 있다.
사실상 이름이 있는 것이나 다름 없어보이지만 하지만 함수도 객체이므로 진짜 이름(식별자)를 가진 것이 아니라 이름처럼 보이는 속성이 있을 뿐이다. 때문에
객체는 단순히 힙(heap) 메모리에 저장된 데이터 덩어리일 뿐이고 변수를 통해 참조될 때에만 의미를 가진다.
따라서 어떤 변수에도 참조되지 않는 객체는 결국 메모리상에서 의미 없는 데이터 덩어리가 된다.
이런 경우 그냥 메모리의 공간만을 차지하고 있기 때문에 가비지 컬렉션 대상이 된다.
4. 화살표 함수에서의 "this"
const myObject = {
myArrowMethod: () => {
console.log(this); // 화살표 함수 (상위 스코프의 this, 여기서는 전역 객체)
// - 화살표 함수는 자신만의 this가 없고, 상위 스코프의 this를 상속받음
}
};
myObject.myArrowMethod();
5. 생성자 함수에서의 "this"
function MyConstructor() {
this.myProperty = "hello";
console.log(this);
}
const newObject = new MyConstructor(); // 새로 생성된 객체 출력
6. 이벤트 핸들러에서의 "this"
<button ID="myButton">탭 1</button>
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', function() {
console.log(this); // 이벤트 핸들러 (이벤트를 발생시킨 요소, 여기서는 myButton) - 이벤트가 발생한 DOM 요소
});
// 전역 컨텍스트
console.log(this); // 브라우저 환경: window, Node.js 환경: global (전역 객체)
function myFunction() {
console.log(this); // 일반 함수 호출 (엄격 모드: undefined, 비엄격 모드: 전역 객체)
}
myFunction();
const myObject = {
myMethod: function() {
console.log(this); // 메서드 호출 (myObject) - 해당 메서드를 호출한 객체
},
myArrowMethod: () => {
console.log(this); // 화살표 함수 (상위 스코프의 this, 여기서는 전역 객체) - 화살표 함수는 자신만의 this가 없고, 상위 스코프의 this를 상속받음
}
};
myObject.myMethod();
myObject.myArrowMethod();
function MyConstructor() {
this.myProperty = 'Hello';
console.log(this); // 생성자 함수 호출 (새로 생성된 인스턴스) - 생성자 함수로 생성된 객체 인스턴스
}
const myInstance = new MyConstructor();
console.log(myInstance.myProperty);
const myButton = document.getElementById('myButton');
myButton.addEventListener('click', function() {
console.log(this); // 이벤트 핸들러 (이벤트를 발생시킨 요소, 여기서는 myButton) - 이벤트가 발생한 DOM 요소
});
function myFunc(a, b) {
console.log(this, a, b);
}
myFunc.call({ x: 1 }, 2, 3); // call (명시적으로 this를 { x: 1 }로 설정) - 첫 번째 인수로 전달된 객체를 this로 바인딩
myFunc.apply({ x: 1 }, [2, 3]); // apply (call과 유사하지만 인수를 배열로 전달) - call과 동일하게 this를 설정하지만, 인수를 배열로 전달
const boundFunc = myFunc.bind({ x: 1 }, 2, 3); // bind (this를 { x: 1 }로 설정하고 인수를 고정한 새 함수 반환) - this와 인수를 미리 바인딩하여 새로운 함수를 생성
boundFunc();