자바스크립트 this 란?

this?

Mon, 11 Nov 2019

js

Javascript의 this

자바스크립트의 this 키워드는 다른언어와 조금 다르게 동작합니다.

대부분의 경우 this 의 값은 함수를 호출한 방법이 결정합니다. ES5에서는 함수를 어떤식으로 호출했는지 상관없이 this값을 설정할 수 있는 bind 메서드를 추가했고, ES6에서는 스스로의 this 바인딩을 제공하지 않는 Arrow Funtion, 화살표 함수를 추가했습니다.

먼저 this가 어떤 값인지 알아보려면 크롬브라우저에서 F12를 눌러 개발자도구의 Console 탭으로 이동하여 직접 입력해보겠습니다.

console.log(this)
// Window { ... }

console.log(this === window)
// true

위의 결과에서 알수있듯 자바스크립트의 this는 기본적으로 window 입니다. 하지만 this가 window가 아닌 경우가 있기 때문에 이 부분을 확인해 보아야 합니다.

전역 스코프

전역스코프에서의 this는 strict 모드와 관계없이 항상 전역객체를 참조합니다.

this.a = 'byseop';
console.log(window.b); // 'byseop'
console.log(b); // 'byseop'

함수 스코프

함수스코프의 this는 함수를 호출한 방법에 의해 결정됩니다.

단순 함수에서의 호출

function a() {
    return this;
}
a() === window; // true

function b() {
    "use strict"
    return this;
}
b() === undefined; // true

위의 a함수와 b함수의 차이점은 “use strict” 엄격모드의 차이입니다. 일반적인 함수의 this는 window 이지만, 엄격모드일때 함수의 this는 undefined 가 됩니다.

함수의 this가 함수 자신을 가르키게 하려면 call() 혹은 apply 를 이용합니다.

var obj = {a: 'Custom'};

var a = 'Global';

function whatsThis() {
    return this.a;
}

whatsThis(); // 'Global'
whatsThis.call(obj); // 'Custom'
whatsThis.apply(obj); // 'Custom'

그리고 ES5 에서부터 도입된 Function.prototype.bind() 가 있습니다.

this.x = 9;
var module = {
    x: 81,
    getX: function() { return this.x; }
};

module.getX(); // 81

var retrieveX = module.getX;
retrieveX(); // 9, 함수가 전역스코프에서 호출됨

var boundGetX = retrieve.bind(module);
boundGetX(); // 81, module과 바인딩 된 this 가 있는 함수 생성

이런식으로 객체와 바인딩 된 함수를 생성할 수 있습니다.

그리고 ES6 에서 도입된 화살표 함수 를 이용하는 방법도 있습니다. 화살표 함수의 경우 사용해서는 안되는 경우도 몇가지 있기때문에 링크에서 확인해보아야 합니다. (메서드, 프로토타입, 생성자함수, 이벤트리스너의 콜백함수)

var globalObject = this;
var foo = () => this;

console.log(globalObject === foo()); // true

화살표함수도 전역스코프에서 this를 조회하면 일반함수와 마찬가지로 window를 가르킵니다.
다음 예제를 확인해보세요

var obj = {
    bar: function() { // (익명함수 A)
        var x = () => this; // (익명함수 B)
        return x;
    }
}

var fn = obj.bar();
console.log(fn() === obj); // true, 화살표 함수를 이용하면 obj를 바인딩한다.

var fn2 = obj.bar; // fn과 차이점은 obj를 참조하기만 한 것이다.
console.log(fn2()()); // Window { ... }

fn 경우 obj.bar에 할당된 함수(익명함수 A)는 화살표 함수로 생성된 또다른 함수(익명함수 B)를 반환합니다. 그 결과 함수 B의 thisobj.bar(함수 A)의 this로 영구적으로 설정됩니다.

하지만 fn2의 경우 함수 A 자체를 참조하여 전역스코프에서의 화살표함수처럼 this가 window를 가르키게 됩니다.

객체의 메서드에서의 호출

함수를 어떤 객체의 메서드로 호출하면 this는 그 객체를 가르킵니다.

var o = {
    prop: 37,
    f: function() {
        return this.prop; // 여기서 this는 객체를 가르킴
    }
};

console.log(o.f()); // 37

위의 this는 객체를 가르키게 됩니다. o.f()를 실행할 때 o 객체가 함수 내부의 this와 바인딩됩니다.
이와 같이 객체의 메서드의 this는 객체를 가르키게 됩니다. 그런데 이 메서드의 this는 정의 방법이나 순서에 영향을 받지 않습니다. 다시말하면 객체를 미리 선언하고 이후에 메서드를 추가해도 상관이 없습니다.

function independent() {
    return this.prop;
}

o.f = independent;

console.log(o.f()); // 37

함수스코프의 this는 호출 방법에 따라 결정된 다는 말이 기억나시나요? 위의 예제처럼 정의 방법이나 순서에 상관없이 객체 o의 메서드 f에서의 this는 그 객체 o를 가르키게 됩니다. 그럼 다음 예제를 볼까요?

o.b = {
    g: independent,
    prop: 42
}

console.log(o.b.g()); // 42
console.log(o);
// o: {
//     f: independent,
//     prop: 37,
//     b: {
//         g: independent,
//         prop: 42
//     }
// }

마찬가지로, o.b.g()this는 자기 자신 o.b의 객체를 가르킵니다. bo 객체의 값이라는 것은 중요하지 않습니다. o.b.g() 가 실행 될 때 thiso.b를 가르키게 됩니다.

생성자에서의 호출

함수를 new 키워드와 함께 생성자로 사용하면 this를 새로 생긴 객체에 바인딩합니다.

function C() {
    this.a = 37;
}

var o = new C();
console.log(o.a); // 37

function C2() {
    this.a = 37;
    return {a: 38};
}
o = new C2();
console.log(o.a); // 38

이렇게 함수를 생성자함수로 생성하게 되면 this가 자동으로 그 객체에 바인딩 됩니다.

DOM 이벤트리스너에서의 호출

document.body.onclick = function() {
    console.log(this); // <body></body>
}
document.querySelector('body').addEventListener('click', function() {
    console.log(this); // <body></body>
}, false)

이벤트리스너의 this는 이벤트가 적용된 엘리먼트를 가르킵니다.

인라인 이벤트 핸들러에서의 호출

<button onclick="alert(this.tagName.toLowerCase());">
  this 확인
</button>

위의 alert는 button을 보여줍니다. 코드를 인라인 이벤트핸들러로 사용하게되면, this는 이벤트핸들러가 연결된 DOM을 가르킵니다.

마무리

javascript의 헷갈리는 this는 호출방법에 따라 달라진다는 것을 유의하면 실수를 줄일수 있을것 같습니다.

Reference

Loading...
byseop

BYSEOP 안녕하세요. BYSEOP입니다.
제 글을 읽어주셔서 감사합니다. 도움이 되셨다면 위쪽에 SHARE를 이용해주세요!
궁금한점은 댓글로 남겨주세요. 감사합니다!

  • this is a personal blog built by byseop
  • GatsbyJS, ReactJs, CSS in JS
  • deliverd by Netlify