게시글

5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭
강좌58 - JavaScript - 6년 전 등록 / 3년 전 수정

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

안녕하세요. 이번 시간에는 자바스크립트 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라는 것 알아두세요. (strict 모드에서는 undefined !!)

조회수:
0
목록
투표로 게시글에 관해 피드백을 해주시면 게시글 수정 시 반영됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright 2016- . 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.
5만명이 선택한 평균 별점 4.9의 제로초 프로그래밍 강좌! 로드맵만 따라오면 됩니다! 클릭

댓글

14개의 댓글이 있습니다.
8달 전
좋은 글 감사합니다!
질문이 있습니다.
그냥 this를 작성하면, strict 모드일 경우는 undefined라고 하셨는데
'use strict'를 사용해서 strict모드일 경우에도 window객체라고 알고있습니다!
window객체가 맞지 않나요~?
3년 전
이 짧은 글로.. this를 어렴풋이나마 이해하게 되었습니다.. 명강의입니다! 감사합니다!!
4년 전
아 이걸 지금 봤네
5년 전
addEventListener나 jquery 함수로 인해 this가 window가 아닌 내부로직으로 변경된다고 하셨습니다만 그게 아니라 콜백함수로 실행하므로 당연히 함수실행주체가 this는 window가 아닌 것으로 변경되는거 같습니다
그렇게 치면 굳이 외워야 된다거나 내부로직으로 변경되는게 아닌 당연한 결과인듯 합니다
일례로, element.onclick 요소에 곧바로 function을 주입하면 당연히 this는 element이겠지만 foo() 함수를 따로 정의한 뒤 element.onclick = foo로 함수실행주소를 할당해주어 this가 element로 지정되는 것과 같은 이치에서 말입니다
5년 전
콜백함수라고 this가 반드시 그 요소인것은 아닙니다. this는 정하기나름입니다.
5년 전
안녕하세요?

감사합니다.
5년 전
와, 이해하기 좋게 써주셨네요. 감사합니다 :D
5년 전
ES5 function의 this하고 ES6 () => function의 this가 왜 다른지 궁금합니다.

#ES5
function callFunc(){
return {
foo : 25,
bar : function(){
console.log(this.foo);
}
}
}

callFunc.call({foo:100}).bar(); // 25

#ES6
function callFunc(){
return {
foo : 25,
bar : () => {
console.log(this.foo);
}
}
}

callFunc.call({foo:100}).bar(); // 100
5년 전
this를 다르게 바인딩하기 때문입니다. 이건 ecmascript 화살표 함수 글을 확인하세요. 화살표함수는 외부의 this를 반영합니다.
6년 전
이걸 왜 지금 발견했을까요 ㅠㅠㅠ 인프런 보고 방문합니다 ㅠㅠㅠㅠㅠ
6년 전
안녕하세요 제로초님 node.js교과서 책 샀다가 여기까지 오게되었습니다. 자바스크립트에 대한 지식이 없어서 빨리 공부하고 노드 공부하고싶은데요 혹시 추천해줄 책이 있으신가요? 글을 따로 적을데가 없어서 여기에 여쭙습니다
6년 전
사실 제 블로그 정도만 보셔도 됩니다. 책은 러닝 자바스크립트나 인사이드자바스크립트처럼 조금 어려운 책을 보시는 것을 추천드립니다.
6년 전
감사합니다!!!
6년 전
스트릭 모드에서의 this에 대한 설명도 있으면 좋을거같아요 !
6년 전
실행 컨텍스트 강좌 링크가 잘못됐습니다. 이 글 자체 링크가 걸려있습니다.
6년 전
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 이 잘 안되네요 ㅠㅠ
6년 전
간단합니다. sum: function은 객체의 메서드이고, calculate는 질문자분이 직접 선언한 함수이기 때문입니다. 함수를 직접 선언하면 this는 윈도우입니다. 이 부분이 사람을 많이 헷갈리게 하는 것이죠. this는 하위 함수(애로우 함수가 아닌)로 상속되지 않습니다.
6년 전
감사합니다.ㅎㅎ
6년 전
와우 너무 너무 잘 보고 잘 이해하고 갑니다. 너무 쉽게 설명 주셔서 감사합니다.