Core JavaScript 5주차

Junyeol·2025년 9월 6일

Core JavaScript

목록 보기
6/8
post-thumbnail

this가 뭔가요?

다른 언어에서 this는 클래스 안에서만 쓰이지만, JavaScript에서는 어디서든 this를 쓸 수 있어요. 그래서 헷갈리죠!

핵심은 "this는 함수가 호출되는 순간에 결정된다"는 것입니다. 함수를 어떻게 호출하느냐에 따라 this가 가리키는 대상이 달라져요.

// 같은 함수라도 호출 방식에 따라 this가 달라집니다
function 누구냐너() {
  console.log('나는', this);
}

누구냐너(); // 전역 객체 (Window)
const 객체 = { 메서드: 누구냐너 };
객체.메서드(); // 객체 자신

상황별 this 완전 정복

1. 전역 공간에서의 this

console.log(this); // 브라우저: Window 객체, Node.js: global 객체

// 전역 변수와 전역 객체의 관계
var 전역변수 = '안녕하세요';
console.log(전역변수);        // '안녕하세요'
console.log(window.전역변수);  // '안녕하세요'
console.log(this.전역변수);    // '안녕하세요'

핵심: 전역 공간에서 this는 전역 객체를 가리킵니다. 그래서 전역 변수를 선언하면 자동으로 전역 객체의 프로퍼티가 됩니다.

2. 메서드로 호출할 때의 this

const 카페 = {
  이름: '스타벅스',
  인사하기: function() {
    console.log(`안녕하세요, ${this.이름}입니다!`);
  }
};

카페.인사하기(); // "안녕하세요, 스타벅스입니다!"

핵심: 메서드로 호출하면 점(.) 앞의 객체가 this가 됩니다.

3. 함수로 호출할 때의 this

function 일반함수() {
  console.log(this); // Window 객체
}

일반함수(); // 함수로 호출하면 this는 전역 객체

주의사항: 이건 JavaScript의 설계 실수로 여겨집니다. 예상과 다르게 동작할 수 있어요!

4. 메서드 안의 내부 함수 - 함정 주의! ⚠️

const 객체 = {
  이름: '외부',
  외부메서드: function() {
    console.log('외부 this:', this.이름); // '외부'
    
    function 내부함수() {
      console.log('내부 this:', this.이름); // undefined (전역 객체)
    }
    
    내부함수(); // 함수로 호출되어 this가 전역 객체
    
    // 해결방법 1: 화살표 함수 사용
    const 화살표함수 = () => {
      console.log('화살표 this:', this.이름); // '외부'
    };
    화살표함수();
    
    // 해결방법 2: 다른 객체의 메서드로 만들기
    const 내부객체 = {
      메서드: 내부함수
    };
    내부객체.메서드(); // 내부객체가 this
  }
};

객체.외부메서드();

화살표 함수의 특별함: 화살표 함수는 자신만의 this를 만들지 않고, 상위 스코프의 this를 그대로 사용합니다.

5. 콜백 함수에서의 this

// 기본적으로 전역 객체를 가리킴
[1, 2, 3].forEach(function(item) {
  console.log(this); // Window 객체
});

// 하지만 일부 메서드는 this를 지정해줌
document.getElementById('버튼').addEventListener('click', function() {
  console.log(this); // 클릭된 버튼 엘리먼트
});

핵심: 콜백 함수의 this는 그 함수를 호출하는 메서드가 결정합니다.

6. 생성자 함수에서의 this

function 고양이(이름, 나이) {
  this.울음소리 = '야옹';
  this.이름 = 이름;
  this.나이 = 나이;
}

const 초코 = new 고양이('초코', 7);
console.log(초코); 
// 고양이 {울음소리: '야옹', 이름: '초코', 나이: 7}

핵심: new 키워드로 호출하면 this는 새로 만들어질 인스턴스를 가리킵니다.

this를 내가 원하는 대로 조종하기 🎮

1. call 메서드 - 즉시 실행

const 객체1 = { 이름: '철수' };
const 객체2 = { 이름: '영희' };

function 자기소개(나이, 취미) {
  console.log(`안녕하세요, 저는 ${this.이름}이고 ${나이}살이며 ${취미}를 좋아해요.`);
}

자기소개.call(객체1, 25, '독서');
// "안녕하세요, 저는 철수이고 25살이며 독서를 좋아해요."

자기소개.call(객체2, 23, '영화감상');
// "안녕하세요, 저는 영희이고 23살이며 영화감상을 좋아해요."

2. 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']

3. 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 상황들

이벤트 핸들러에서의 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 객체 (주의!)
});

배열 메서드에서 this 지정하기

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

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

화살표 함수의 특별함:

  • 자신만의 this를 만들지 않음
  • 상위 스코프의 this를 그대로 사용
  • call/apply/bind로도 this를 바꿀 수 없음

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

profile
천천히

0개의 댓글