내용이 안 보인다면 쿠키/캐시를 지우고 새로고침 하세요!
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌58 - JavaScript - 7달 전 등록 / 4달 전 수정

자바스크립트의 this는 무엇인가?

조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

안녕하세요. 이번 시간에는 자바스크립트 this에 대해 알아보겠습니다. 사실 이미 실행 컨텍스트 강좌 에 다 설명해둔 것이긴 한데요. 그 강좌는 실행 컨텍스트(전체적인 흐름)에 더 집중해서 쓰여진 것이기 때문에 이번 시간에는 this만 따로 알아봅니다. 실행 컨텍스트가 너무 어려워서 잘 와닿지 않는다는 분들은 이 글만 읽으시면 됩니다(그래도 결국 실행 컨텍스트는 알아야 합니다).

자세한 스펙 설명같은 것은 다 제쳐둡니다. 저는 스펙 읽는 것을 좋아하진 않거든요. 인터넷 강의 스타일 식으로 쉽게 외울 수 있게 해드리겠습니다.

브라우저 콘솔(F12)을 켜고, this를 쳐봅시다.

this; // Window {}

네 window네요. 함수 안에 넣어서 해봅시다.

function a() { console.log(this); };
a(); // Window {}

네. window네요(strict 모드일 경우는 undefined). 자, this는 window였습니다! 이상으로 강좌를 마무리합니다. ㅎㅎ

장난이고요. 여기서 한 가지 사실을 알 수 있습니다. this는 기본적으로 window입니다. 그렇다면 this가 window가 아닌 경우를 외우면 되겠죠?

객체의 메서드를 봅시다.

var obj = {
  a: function() { console.log(this); },
};
obj.a(); // obj

객체 메서드 a 안의 this는 객체를 가리킵니다. 이것은 객체의 메서드를 호출할 때 this를 내부적으로 바꿔주기 때문에 그렇습니다.

단 위의 예제에서 다음과 같이 하면 결과가 달라집니다.

var a2 = obj.a;
a2(); // window

호출할 때, 호출하는 함수가 객체의 메서드인지 그냥 함수인지가 중요합니다. a2는 obj.a를 꺼내온 것이기 때문에 더 이상 obj의 메서드가 아닙니다.

var obj2 = { c: 'd' };
function b() {
  console.log(this);
}
b(); // Window
b.bind(obj2).call(); // obj2
b.call(obj2); // obj2 
b.apply(obj2); // obj2

명시적으로 this를 바꾸는 함수 메서드 삼총사 bind, call, apply를 사용하면 this가 객체를 가리킵니다.

이제 마지막으로 생성자의 경우입니다. 처음부터 공포스러운 this를 쓰는 무시무시한 친구입니다.

function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.sayHi = function() {
  console.log(this.name, this.age);
}

생성자 함수도 함수라는 것 알고 계시죠? 만약 new로 호출하지 않고 그냥 호출한다면 어떻게 될까요?

Person('ZeroCho', 25);
console.log(window.name, window.age); // ZeroCho 25

그냥 함수에서 this가 window를 가리킨다고 했죠? 그래서 this.name 과 this.age는 window.name, window.age가 되어버립니다.

이를 막으려면 new Person을 사용해야 합니다.

var hero = new Person('Hero', 33); // Person {name: "Hero", age: 33}
hero.sayHi(); // Hero 33

이렇게 new를 붙이면 this가 생성자를 통해 생성된 인스턴스(hero 자신)가 됩니다. 

실수로 new를 안 붙이는 문제를 막는 장치가 class가 ES6에서 추가되었습니다.

여기까지는 대부분 다 이해하시고 계실 겁니다. 그런데 실무에서, 이벤트리스너나 제이쿼리같은 것을 썼을 때가 문제입니다. 리액트도 마찬가지고요.

document.body.onclick = function() {
  console.log(this); // <body>
}

아니, 이건 그냥 함수인데 this가 window가 아니라 <body>입니다. 객체 메서드도 아니고, bind한 것도 아니고, new 붙인 것도 아닌데 말이죠. 누가 바꿨을까요? 바로 이벤트가 발생할 때, 내부적으로 this가 바뀐 것입니다. 내부적으로 바뀐 것이기 때문에 동작을 외울 수밖에 없습니다. ㅠㅠ

$('div').on('click', function() {
  console.log(this);
});

이런 제이쿼리 코드 많이 보셨죠? this는 클릭한 div가 됩니다. 왜 그렇냐고요? 내부적으로 function을 호출할 때 그렇게 this를 바꿔버렸습니다. 이런 건 어쩔 수 없이 외워야 합니다.

$('div').on('click', function() {
  console.log(this); // <div>
  function inner() {
    console.log('inner', this); // inner Window
  }
  inner();
});

사람들을 미치게 하는 응용사례입니다. 방금 전 클릭 이벤트에서 제이쿼리가 내부적으로 this를 바꿔버린다고 했습니다. 근데 inner 함수 호출 시에는 this가 window입니다.

inner는 죄가 없습니다. 함수의 this는 기본적으로 window라는 원칙을 충실히 따른 것이죠. 그저 click 이벤트 리스너가 잘못한 겁니다(잘못했다기 보다는 내부적으로 this를 바꿨음에도 명시적으로 알리지 않은 것).

위의 문제를 해결하기 위해서는

$('div').on('click', function() {
  console.log(this); // <div>
  var that = this;
  function inner() {
    console.log('inner', that); // inner <div>
  }
  inner();
});

위처럼 this를 that이라는 변수에 저장하든지

$('div').on('click', function() {
  console.log(this); // <div>
  const inner = () => {
    console.log('inner', this); // inner <div>
  }
  inner();
});

ES6 화살표 함수를 씁니다. ES6 화살표 함수는 this로 window 대신 상위 함수의 this를 가져옵니다(여기서는 <div>)

자, 이렇게 this에 대해서 알아보았습니다. 왜 함수의 this가 기본적으로 window인지는 실행 컨텍스트를 알아보셔야 하고요.

다시 한 번, 정리하자면, this는 기본적으로 window이지만, 객체 메서드, bind call apply, new일 때 this가 바뀝니다. 그리고 이벤트리스너나 기타 라이브러리처럼 this를 내부적으로 바꿀 수도 있으니 항상 this를 확인해보셔야 하고요. 여러분이 선언한 function의 this는 항상 window라는 것 알아두세요.

투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

6개의 댓글이 있습니다.
2달 전
안녕하세요 제로초님 node.js교과서 책 샀다가 여기까지 오게되었습니다. 자바스크립트에 대한 지식이 없어서 빨리 공부하고 노드 공부하고싶은데요 혹시 추천해줄 책이 있으신가요? 글을 따로 적을데가 없어서 여기에 여쭙습니다
2달 전
사실 제 블로그 정도만 보셔도 됩니다. 책은 러닝 자바스크립트나 인사이드자바스크립트처럼 조금 어려운 책을 보시는 것을 추천드립니다.
2달 전
감사합니다!!!
5달 전
스트릭 모드에서의 this에 대한 설명도 있으면 좋을거같아요 !
6달 전
실행 컨텍스트 강좌 링크가 잘못됐습니다. 이 글 자체 링크가 걸려있습니다.
7달 전
var numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers); // => true
function calculate() {
// this는 window, 엄격 모드였으면 undefined
console.log(this === numbers); // => false
return this.numberA + this.numberB; // numbers에 접근 불가
}
return calculate();
}
};
// 이는 객체 내에 있는 메소드를 실행하는 것. 이 실행시의 this는 window
numbers.sum(); // NaN, 엄격 모드였으면 TypeError

안녕하세요 좋은 글 또 감사합니다! 위에 코드에서 calculate 함수 안에서 this 가 왜 window 인지 이해가 되지 않습니다 ㅠ numbers여야 하는것 아닌가요? 항상 감사드립니다
indentation 이 잘 안되네요 ㅠㅠ
7달 전
간단합니다. sum: function은 객체의 메서드이고, calculate는 질문자분이 직접 선언한 함수이기 때문입니다. 함수를 직접 선언하면 this는 윈도우입니다. 이 부분이 사람을 많이 헷갈리게 하는 것이죠. this는 하위 함수(애로우 함수가 아닌)로 상속되지 않습니다.
7달 전
감사합니다.ㅎㅎ
7달 전
와우 너무 너무 잘 보고 잘 이해하고 갑니다. 너무 쉽게 설명 주셔서 감사합니다.