본문으로 건너뛰기

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 결정 규칙 (우선순위 순):

  1. new 함수() → 새로 생성된 인스턴스
  2. call/apply/bind → 직접 지정한 객체
  3. 객체.메서드() → 점 앞의 객체
  4. 함수() → 전역 객체 (또는 strict mode에서 undefined)

화살표 함수의 특별함:

  • 자신만의 this를 만들지 않음
  • 상위 스코프의 this를 그대로 사용
  • call/apply/bind로도 변경 불가

🎯 정리

  • this는 함수가 정의될 때가 아니라 호출될 때 결정됩니다
  • 호출 방식에 따라 this가 가리키는 대상이 달라집니다
  • call/apply/bind로 this를 명시적으로 지정할 수 있습니다
  • 화살표 함수는 렉시컬 this를 사용합니다
  • 콜백 함수에서 this 문제를 해결하는 여러 방법이 있습니다

기억하세요: this는 함수가 정의될 때가 아니라 호출될 때 결정됩니다! 🎯


스터디: 코어 자바스크립트 5주차