this
· 약 5분
코어 자바스크립트 스터디 5주차 내용을 정리했습니다. 이번 주제는 this에 대한 모든 것입니다.
🎯 this의 핵심 개념
다른 언어에서 this는 클래스 안에서만 쓰이지만, JavaScript에서는 어디서든 this를 쓸 수 있어요. 그래서 헷갈리죠!
핵심: "this는 함수가 호출되는 순간에 결정된다"는 것입니다. 함수를 어떻게 호출하느냐에 따라 this가 가리키는 대상이 달라져요.
// 같은 함수라도 호출 방식에 따라 this가 달라집니다
function 누구냐너() {
console.log("나는", this);
}
누구냐너(); // 전역 객체 (Window)
const 객체 = { 메서드: 누구냐너 };
객체.메서드(); // 객체 자신
🌍 전역 공간에서의 this
console.log(this); // 브라우저: Window 객체, Node.js: global 객체
// 전역 변수와 전역 객체의 관계
var 전역변수 = "안녕하세요";
console.log(전역변수); // '안녕하세요'
console.log(window.전역변수); // '안녕하세요'
console.log(this.전역변수); // '안녕하세요'
핵심: 전역 공간에서 this는 전역 객체를 가리킵니다. 그래서 전역 변수를 선언하면 자동으로 전역 객체의 프로퍼티가 됩니다.
📦 메서드로서 호출될 때의 this
const 카페 = {
이름: "스타벅스",
인사하기: function () {
console.log(`안녕하세요, ${this.이름}입니다!`);
},
};
카페.인사하기(); // "안녕하세요, 스타벅스입니다!"
핵심: 메서드로 호출하면 점(.) 앞의 객체가 this가 됩니다.
🔧 함수로서 호출될 때의 this
function 일반함수() {
console.log(this); // Window 객체
}
일반함수(); // 함수로 호출하면 this는 전역 객체
주의사항: 이건 JavaScript의 설계 실수로 여겨집니다. 예상과 다르게 동작할 수 있어요!
메서드 내부 함수에서의 this 문제
const 객체 = {
이름: "외부",
외부메서드: function () {
console.log("외부 this:", this.이름); // '외부'
function 내부함수() {
console.log("내부 this:", this.이름); // undefined (전역 객체)
}
내부함수(); // 함수로 호출되어 this가 전역 객체
// 해결방법 1: 화살표 함수 사용
const 화살표함수 = () => {
console.log("화살표 this:", this.이름); // '외부'
};
화살표함수();
// 해결방법 2: 다른 객체의 메서드로 만들기
const 내부객체 = {
메서드: 내부함수,
};
내부객체.메서드(); // 내부객체가 this
},
};
객체.외부메서드();
화살표 함수의 특별함: 화살표 함수는 자신만의 this를 만들지 않고, 상위 스코프의 this를 그대로 사용합니다.
📞 콜백 함수 호출 시의 this
// 기본적으로 전역 객체를 가리킴
[1, 2, 3].forEach(function (item) {
console.log(this); // Window 객체
});
// 하지만 일부 메서드는 this를 지정해줌
document.getElementById("버튼").addEventListener("click", function () {
console.log(this); // 클릭된 버튼 엘리먼트
});
핵심: 콜백 함수의 this는 그 함수를 호출하는 메서드가 결정합니다.
🏗️ 생성자 함수 호출 시의 this
function 고양이(이름, 나이) {
this.울음소리 = "야옹";
this.이름 = 이름;
this.나이 = 나이;
}
const 초코 = new 고양이("초코", 7);
console.log(초코);
// 고양이 {울음소리: '야옹', 이름: '초코', 나이: 7}
핵심: new
키워드로 호출하면 this는 새로 만들어질 인스턴스를 가리킵니다.
🎛️ call/apply 메서드로 this 바인딩
call 메서드
const 객체1 = { 이름: "철수" };
const 객체2 = { 이름: "영희" };
function 자기소개(나이, 취미) {
console.log(
`안녕하세요, 저는 ${this.이름}이고 ${나이}살이며 ${취미}를 좋아해요.`
);
}
자기소개.call(객체1, 25, "독서");
// "안녕하세요, 저는 철수이고 25살이며 독서를 좋아해요."
자기소개.call(객체2, 23, "영화감상");
// "안녕하세요, 저는 영희이고 23살이며 영화감상을 좋아해요."
apply 메서드
function 합계계산(a, b, c) {
console.log(`${this.이름}의 계산 결과:`, a + b + c);
}
const 계산기 = { 이름: "스마트 계산기" };
// call과 apply의 차이점
합계계산.call(계산기, 1, 2, 3); // 인수를 하나씩
합계계산.apply(계산기, [1, 2, 3]); // 인수를 배열로
실용 예제: 유사배열객체에 배열 메서드 사용
// 유사배열객체 (Array-like Object)
const 유사배열 = {
0: "a",
1: "b",
2: "c",
length: 3,
};
// 배열 메서드를 빌려 쓰기
Array.prototype.push.call(유사배열, "d");
console.log(유사배열);
// {0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4}
const 진짜배열 = Array.prototype.slice.call(유사배열);
console.log(진짜배열); // ['a', 'b', 'c', 'd']
🔒 bind 메서드
function 인사(인사말, 마무리) {
console.log(`${인사말}, 저는 ${this.이름}입니다. ${마무리}!`);
}
const 사람 = { 이름: "김철수" };
// this만 고정
const 철수인사 = 인사.bind(사람);
철수인사("안녕하세요", "만나서 반가워요");
// "안녕하세요, 저는 김철수입니다. 만나서 반가워요!"
// this + 일부 인수까지 고정 (부분 적용 함수)
const 철수안녕 = 인사.bind(사람, "안녕하세요");
철수안녕("좋은 하루 되세요");
// "안녕하세요, 저는 김철수입니다. 좋은 하루 되세요!"
// bind로 만든 함수는 이름에 'bound'가 붙음
console.log(인사.name); // 'insa'
console.log(철수인사.name); // 'bound insa'
실용 예제: setTimeout에서 this 문제 해결
const 타이머객체 = {
메시지: "시간이 됐습니다!",
지금알림: function () {
console.log(this.메시지);
},
나중알림1: function () {
// 문제: setTimeout의 콜백은 전역 객체를 this로 함
setTimeout(this.지금알림, 1000); // undefined
},
나중알림2: function () {
// 해결: bind로 this 고정
setTimeout(this.지금알림.bind(this), 1000); // '시간이 됐습니다!'
},
나중알림3: function () {
// 해결: 화살표 함수 사용
setTimeout(() => this.지금알림(), 1000); // '시간이 됐습니다!'
},
};
타이머객체.나중알림1(); // undefined 출력
타이머객체.나중알림2(); // '시간이 됐습니다!' 출력
타이머객체.나중알림3(); // '시간이 됐습니다!' 출력
⚡ 화살표 함수의 특별한 this
// HTML: <button id="버튼">클릭하세요</button>
document.getElementById("버튼").addEventListener("click", function () {
console.log(this); // 버튼 엘리먼트
this.style.color = "red"; // 버튼 글자색이 빨간색으로 변경
});
// 화살표 함수를 쓰면?
document.getElementById("버튼").addEventListener("click", () => {
console.log(this); // Window 객체 (주의!)
});
thisArg 매개변수 활용
const 학급 = {
이름: "3학년 1반",
학생들: ["철수", "영희", "민수"],
출석체크: function () {
// thisArg 매개변수로 this 지정
this.학생들.forEach(function (학생) {
console.log(`${this.이름}: ${학생} 출석`);
}, this); // 여기서 this를 전달!
// 또는 화살표 함수 사용
this.학생들.forEach((학생) => {
console.log(`${this.이름}: ${학생} 출석`);
});
},
};
학급.출석체크();
// "3학년 1반: 철수 출석"
// "3학년 1반: 영희 출석"
// "3학년 1반: 민수 출석"
🐛 this 디버깅 팁
// this가 예상과 다르게 동작할 때 체크 포인트
function 디버깅함수() {
console.log("함수명:", arguments.callee.name);
console.log("호출 방식:", this === window ? "함수 호출" : "메서드 호출");
console.log("this 값:", this);
console.log("---");
}
const 객체 = { 메서드: 디버깅함수 };
디버깅함수(); // 함수 호출
객체.메서드(); // 메서드 호출
디버깅함수.call(객체); // call로 호출
📝 this 결정 규칙 정리
this 결정 규칙 (우선순위 순):
- new 함수() → 새로 생성된 인스턴스
- call/apply/bind → 직접 지정한 객체
- 객체.메서드() → 점 앞의 객체
- 함수() → 전역 객체 (또는 strict mode에서 undefined)
화살표 함수의 특별함:
- 자신만의 this를 만들지 않음
- 상위 스코프의 this를 그대로 사용
- call/apply/bind로도 변경 불가
🎯 정리
- this는 함수가 정의될 때가 아니라 호출될 때 결정됩니다
- 호출 방식에 따라 this가 가리키는 대상이 달라집니다
- call/apply/bind로 this를 명시적으로 지정할 수 있습니다
- 화살표 함수는 렉시컬 this를 사용합니다
- 콜백 함수에서 this 문제를 해결하는 여러 방법이 있습니다
기억하세요: this는 함수가 정의될 때가 아니라 호출될 때 결정됩니다! 🎯
스터디: 코어 자바스크립트 5주차