자바스크립트 this
, 대체 넌 누구냐? 완벽 이해와 활용 가이드 (초보자를 위한 상세 설명)
안녕하세요, 개발자 여러분! 자바스크립트를 공부하다 보면 가장 많은 질문과 혼란을 야기하는 키워드 중 하나가 바로 this
입니다. 다른 프로그래밍 언어의 this
와는 다르게 동작하는 녀석이라 "도대체 this
는 언제 무엇을 가리키는 거지?"라는 의문을 품게 되죠.
하지만 걱정 마세요! 오늘 이 글을 통해 this
의 모든 것을 파헤쳐 보고, 언제 어떻게 사용해야 하는지 명확하게 알려드리겠습니다. this
개념을 완벽하게 이해하고 나면 자바스크립트 코드의 흐름을 훨씬 더 잘 파악하고 능숙하게 다룰 수 있게 될 겁니다.
📚 목차
1. this
란 무엇인가? (다른 언어와의 차이점)
자바스크립트에서 this
는 함수가 어떻게 호출되었는지에 따라 동적으로 결정되는 특별한 키워드입니다. 이게 핵심입니다! C++, Java 등 다른 언어의 this
가 대개 "현재 객체 인스턴스 자신"을 명확히 가리키는 것과 달리, 자바스크립트의 this
는 함수의 정의 시점이 아닌, 호출 시점의 문맥(Context)에 따라 유연하게 바뀝니다.
이러한 유연성 때문에 처음에는 어렵게 느껴질 수 있지만, 이 특징을 이해하면 자바스크립트의 강력한 동적 특성을 활용할 수 있게 됩니다.
2. this
바인딩 5가지 핵심 규칙
this
가 무엇을 가리킬지는 주로 함수가 호출되는 5가지 방식에 따라 결정됩니다. 이 규칙들을 정확히 아는 것이 this
를 이해하는 지름길입니다.
1) 일반 함수 호출 (Default Binding)
가장 기본적인 형태의 함수 호출입니다. 어떤 객체의 메서드가 아닌, 독립적인 함수로 호출될 때 this
가 어떻게 되는지 살펴봅시다.
- 비엄격 모드 (Non-strict mode):
this
는 전역 객체를 가리킵니다.- 브라우저 환경:
window
객체 - Node.js 환경:
global
객체
- 브라우저 환경:
- 엄격 모드 (Strict mode):
this
는undefined
가 됩니다. 이는 개발자의 실수로 전역 객체의 속성을 오염시키는 것을 방지하기 위한 조치입니다.
function showThis() {
console.log(this);
}
// 브라우저 환경 (비엄격 모드)
showThis(); // ⇨ window 객체
// Node.js 환경 (비엄격 모드)
// showThis(); // ⇨ global 객체
'use strict';
function showThisStrict() {
console.log(this);
}
showThisStrict(); // ⇨ undefined
2) 메서드 호출 (Implicit Binding)
함수가 객체의 프로퍼티로서 호출될 때, 즉 객체.메서드()
형태로 호출될 때 this
는 해당 메서드를 호출한 객체 자체를 가리킵니다. 이 경우가 가장 직관적으로 this
가 사용되는 방식입니다.
const person = {
name: 'Alice',
sayHello: function() {
// 여기서 this는 person 객체를 가리킵니다.
console.log(`Hello, my name is ${this.name}`);
}
};
person.sayHello(); // ⇨ Hello, my name is Alice
// 다른 객체에 할당해도 호출하는 객체에 따라 this가 바뀝니다.
const anotherPerson = {
name: 'Bob',
greet: person.sayHello // 메서드를 다른 객체에 할당
};
anotherPerson.greet(); // ⇨ Hello, my name is Bob (this는 anotherPerson 객체를 가리킴)
3) 생성자 함수 호출 (New Binding)
함수가 new
연산자와 함께 호출될 때 (즉, 생성자 함수로 사용될 때), this
는 새롭게 생성된 인스턴스 객체를 가리킵니다. new
키워드를 사용하면 다음과 같은 일들이 자동으로 일어납니다.
- 새로운 빈 객체가 생성됩니다.
- 이 빈 객체가
this
로 바인딩됩니다. - 생성자 함수의 코드가 실행되면서
this
에 속성이나 메서드가 추가됩니다. - 생성된 객체가 반환됩니다 (명시적으로 다른 객체를 반환하지 않는 한).
function Car(make, model) {
// 여기서 this는 새롭게 생성될 Car 인스턴스를 가리킵니다.
this.make = make;
this.model = model;
this.getInfo = function() {
console.log(`This is a ${this.make} ${this.model}.`);
};
}
const myCar = new Car('Hyundai', 'Sonata'); // new 연산자로 호출
myCar.getInfo(); // ⇨ This is a Hyundai Sonata. (this는 myCar 인스턴스를 가리킴)
const yourCar = new Car('Kia', 'K5');
yourCar.getInfo(); // ⇨ This is a K5 Kia.
4) call
, apply
, bind
를 통한 명시적 바인딩 (Explicit Binding)
때로는 this
의 값을 우리가 원하는 특정 객체로 강제로 지정해야 할 필요가 있습니다. 이때 Function.prototype
에 있는 call()
, apply()
, bind()
메서드를 사용합니다.
call(thisArg, arg1, arg2, ...)
: 함수를 즉시 호출하면서 첫 번째 인수로this
값을 지정합니다. 나머지 인수는 쉼표로 구분하여 전달합니다.apply(thisArg, [argsArray])
: 함수를 즉시 호출하면서 첫 번째 인수로this
값을 지정합니다.call
과 다르게 인수를 배열 형태로 전달합니다.bind(thisArg, arg1, arg2, ...)
: 함수를 호출하지 않고,thisArg
로this
값이 고정된 새로운 함수를 반환합니다. 이 반환된 함수를 나중에 호출할 수 있습니다.
const person1 = { name: 'Alice' };
const person2 = { name: 'Bob' };
function introduce(age, occupation) {
console.log(`Hello, my name is ${this.name}. I am ${age} years old and a ${occupation}.`);
}
// call 사용: person1을 this로, 인수는 개별적으로
introduce.call(person1, 30, 'Engineer'); // ⇨ Hello, my name is Alice. I am 30 years old and a Engineer.
// apply 사용: person2를 this로, 인수는 배열로
introduce.apply(person2, [25, 'Designer']); // ⇨ Hello, my name is Bob. I am 25 years old and a Designer.
// bind 사용: person1을 this로 고정된 새로운 함수 생성
const introduceAlice = introduce.bind(person1, 30); // 30은 미리 고정할 수도 있음
introduceAlice('Student'); // ⇨ Hello, my name is Alice. I am 30 years old and a Student.
// bind 시점에 전달된 인수가 먼저 적용됩니다.
5) 화살표 함수 (Arrow Functions)에서의 this
(Lexical this)
ES6에서 도입된 화살표 함수는 기존 함수와 this
바인딩 방식에서 큰 차이를 보입니다. 화살표 함수는 자신만의 this
바인딩을 생성하지 않습니다. 대신, 자신이 선언(정의)된 스코프의 this
를 그대로 상속받습니다. 이를 "렉시컬 this
"라고 부릅니다.
즉, 화살표 함수는 일반 함수와 달리 호출 시점에 this
가 결정되는 것이 아니라, 정의 시점에 이미 상위 스코프의 this
를 가져와 고정시킵니다.
const obj = {
name: 'Developer',
regularFunction: function() {
console.log('Regular function this:', this.name); // ⇨ obj 객체를 가리킴 (메서드 호출)
},
arrowFunction: () => {
// 화살표 함수는 자신의 스코프를 가지지 않으므로,
// 이 obj의 상위 스코프(이 경우 전역 스코프)의 this를 상속받습니다.
console.log('Arrow function this:', this.name); // ⇨ undefined (브라우저 window.name, Node.js global.name)
},
nested: {
name: 'Nested Developer',
// 이 화살표 함수의 상위 스코프는 obj.nested 이지만,
// obj.nested 자체는 this를 가지지 않으므로 한 단계 더 위(obj)를 보고,
// obj의 this는 전역이므로 결국 전역을 가리키게 됩니다.
arrowFunctionInNested: () => {
console.log('Nested arrow function this:', this.name); // ⇨ undefined
},
regularFunctionInNested: function() {
// 이 일반 함수는 nested 객체의 메서드이므로 this는 nested 객체를 가리킵니다.
// 이 안의 화살표 함수는 상위 스코프인 regularFunctionInNested의 this를 상속받습니다.
const innerArrow = () => {
console.log('Inner arrow function this:', this.name); // ⇨ Nested Developer
};
innerArrow();
}
}
};
obj.regularFunction(); // ⇨ Regular function this: Developer
obj.arrowFunction(); // ⇨ Arrow function this: undefined
obj.nested.arrowFunctionInNested(); // ⇨ Nested arrow function this: undefined
obj.nested.regularFunctionInNested(); // ⇨ Inner arrow function this: Nested Developer
화살표 함수의 이러한 특징은 특히 비동기 콜백 함수나 중첩된 함수 내에서 this
바인딩 문제가 발생할 때 유용하게 사용됩니다.
3. this
, 언제 사용하면 좋을까? (실제 활용 사례)
this
는 자바스크립트에서 객체 지향 프로그래밍과 동적인 코드 작성을 위해 필수적인 요소입니다. 다음 상황들에서 this
가 유용하게 사용됩니다.
- 객체의 메서드 내부에서 해당 객체의 다른 속성/메서드에 접근할 때
- 가장 흔하고 직관적인 사용법입니다. 객체의 메서드가 자신(객체)이 가진 데이터를 다룰 때
this
를 사용합니다.
- 가장 흔하고 직관적인 사용법입니다. 객체의 메서드가 자신(객체)이 가진 데이터를 다룰 때
const user = {
firstName: 'John',
lastName: 'Doe',
fullName: function() {
// user 객체 내의 firstName과 lastName에 this로 접근
return `${this.firstName} ${this.lastName}`;
}
};
console.log(user.fullName()); // ⇨ John Doe
- 생성자 함수(또는 클래스)를 통해 새로운 인스턴스를 만들고, 해당 인스턴스의 속성을 초기화하거나 메서드를 정의할 때
new
키워드를 사용하여 객체를 생성할 때,this
는 생성될 인스턴스 자신을 가리키며, 인스턴스별로 고유한 값을 설정할 수 있게 합니다.
// 생성자 함수
function Product(name, price) {
this.name = name; // 생성될 인스턴스의 name 속성
this.price = price; // 생성될 인스턴스의 price 속성
this.displayInfo = function() {
console.log(`${this.name}: $${this.price}`);
};
}
const laptop = new Product('Laptop', 1200);
laptop.displayInfo(); // ⇨ Laptop: $1200
// 클래스 (ES6)
class Book {
constructor(title, author) {
this.title = title;
this.author = author;
}
getDetails() {
console.log(`${this.title} by ${this.author}`);
}
}
const myBook = new Book('The Great Gatsby', 'F. Scott Fitzgerald');
myBook.getDetails(); // ⇨ The Great Gatsby by F. Scott Fitzgerald
- 이벤트 핸들러에서 이벤트를 발생시킨 DOM 요소에 접근할 때
- DOM 요소를 클릭하거나 다른 이벤트가 발생했을 때, 해당 이벤트를 처리하는 함수 내에서
this
는 기본적으로 이벤트를 발생시킨 바로 그 DOM 요소를 가리킵니다.
- DOM 요소를 클릭하거나 다른 이벤트가 발생했을 때, 해당 이벤트를 처리하는 함수 내에서
<button id="myButton">클릭하세요</button>
<script>
document.getElementById('myButton').addEventListener('click', function() {
// 여기서 this는 클릭된 <button> 요소를 가리킵니다.
console.log(this.id); // ⇨ 'myButton' 출력
this.textContent = '클릭됨!'; // 클릭된 버튼의 텍스트 변경
this.style.backgroundColor = 'lightblue';
});
</script>
call
,apply
,bind
를 사용하여 특정 함수를 특정 객체의 문맥(Context)으로 강제 실행해야 할 때- 특히,
forEach
,map
과 같은 고차 함수(Higher-Order Function)의 콜백 함수 내에서this
바인딩이 원하는 대로 되지 않을 때 유용합니다.
- 특히,
const calculator = {
value: 10,
add: function(num) {
this.value += num;
}
};
function processNumbers(arr) {
// 일반 함수로 콜백을 사용하면 this는 window 또는 undefined를 가리킬 수 있습니다.
// 따라서 calculator 객체의 add 메서드를 호출할 수 없습니다.
// arr.forEach(function(num) {
// this.add(num); // 에러 발생 가능!
// });
// bind를 사용하여 콜백 함수의 this를 calculator 객체로 고정합니다.
arr.forEach(function(num) {
this.add(num);
}.bind(calculator)); // this를 calculator로 명시적으로 바인딩
}
processNumbers([1, 2, 3]);
console.log(calculator.value); // ⇨ 16 (10 + 1 + 2 + 3)
- 화살표 함수를 사용하여 상위 스코프의
this
를 그대로 유지하고자 할 때- 비동기 코드, 콜백 함수, 또는 클래스 내부에서
this
가 갑자기 바뀌는 것을 방지하고자 할 때 화살표 함수가 빛을 발합니다.
- 비동기 코드, 콜백 함수, 또는 클래스 내부에서
class DataFetcher {
constructor() {
this.data = [];
}
fetchData() {
console.log('Outer this (DataFetcher instance):', this); // ⇨ DataFetcher 객체
// 일반 함수를 사용하면, setTimeout 내부의 this는 window (브라우저) 또는 Timer 객체 (Node.js)가 됩니다.
// setTimeout(function() {
// console.log('Inside setTimeout (classic) this:', this); // ⇨ window 또는 Timer
// this.data.push('fetched data'); // ⇨ TypeError: this.data is not a function
// }, 1000);
// 화살표 함수를 사용하면, fetchData() 스코프의 this를 그대로 상속받습니다.
setTimeout(() => {
console.log('Inside setTimeout (arrow) this:', this); // ⇨ DataFetcher 객체
this.data.push('fetched data'); // DataFetcher 인스턴스의 data 배열에 접근 가능
console.log('Updated data:', this.data); // ⇨ Updated data: ["fetched data"]
}, 1000);
}
}
const fetcher = new DataFetcher();
fetcher.fetchData();
4. this
마스터를 위한 팁!
- 가장 중요한 것은
this
가 "어떻게" 호출되었는가를 파악하는 것입니다. 함수가 어디에 정의되었는지는 중요하지 않습니다. - 화살표 함수는
this
바인딩 문제를 해결하는 데 매우 유용합니다. 특히 콜백 함수나 클래스 메서드에서this
를 고정해야 할 때 우선적으로 고려해보세요. - 엄격 모드(
'use strict'
) 사용을 습관화하세요.this
바인딩 관련 실수를 미연에 방지하고 더 예측 가능한 코드를 작성할 수 있습니다. - 콘솔에
console.log(this)
를 찍어보세요.this
의 값이 헷갈릴 때는 항상this
가 무엇을 가리키는지 직접 확인하는 것이 가장 좋은 방법입니다.
마무리하며
자바스크립트의 this
는 처음에는 혼란스러울 수 있지만, 오늘 설명드린 5가지 this
바인딩 규칙과 실제 활용 사례들을 반복해서 익히다 보면 어느새 자유자재로 다루게 될 겁니다. 특히 면접에서 this
에 대한 질문은 단골 문제이니, 이 글의 내용을 충분히 숙지하시어 좋은 결과를 얻으시길 바랍니다!
궁금한 점이 있다면 언제든지 댓글로 남겨주세요!
#자바스크립트 #JavaScript #this #JS_this #this_바인딩 #화살표함수 #콜백함수 #프론트엔드 #개발공부 #코딩 #웹개발 #면접대비 #초보개발자
댓글 없음:
댓글 쓰기