시리즈 - this

15517 단어 JavaScriptJavaScript

INTRO

자바스크립트에서 this라는 개념은 많이 헷갈린다. 그래서 오늘 정리 해볼려고 한다. 다른 객체지향언어에서는 대부분 this는 클래스로 생성한 인스턴스 객체를 의미한다. 하지만 자바스크립트에서의 this는 어디서든 사용을 할 수 있고, 상황에 따라 this가 바라보는 대상이 달라진다.

자바스크립트에서의 this를 한번 알아보도록 하자.

1. ThisBinding

우선 전 글에서 확인할 수 있듯이
JS시리즈 - 실행 컨텍스트

실행컨텍스트에는
1. variableEnvironment
2. LexicalEnvrionent
3. ThisBinding
3가지로 크게 구성된다고 했다.

  • ThisBinding에는 현재 실행 컨텍스트가 참조하는 this에 대한 정보가 담기게 된다.

  • ThisBinding은 실행컨텍스트가 활성화 될때 한다.

  • 실행컨텍스틑 언제 생성되는가? 바로 이 컨텍스트에 해당하는 함수가 호출 되는 순간이다.(전역컨텍스트 제외)

2. this

this는 바로 확인할 수 있는게 아니다.
함수를 호출한 방식에 따라서 this는 얼마든지 달라진다.

즉, 호출하는 방식에 따라 달라진다는 것이다.

this는 크게 5가지 상황에서 바라 볼 수 있다.

  • 전역공간에서
  • 함수로 호출 시
  • 메서드로 호출 시
  • callback 함수로 호출 시
  • 생성자 함수로 호출 시

각각의 상황에서 this가 어떻게 다른지 확인 해보자.

3. 전역공간에서의 this

  • 전역 공간에서 this는 전역 객체를 가리킨다.
console.log(this);
  • 브라우저 환경에서는 window
  • Node.js 환경에서는 global
    이라는 전역객체가 this에 담기게 된다.

4. 함수 호출 방법 : 함수 vs 메서드

어떤 함수를 호출하는 것에는 여러가지 방법이 있지만,
일반적인 방법은 두가지가 있다.

  • 함수로서의 호출

  • 메서드로서의 호출

    함수와 메서드는 미리 정의한 동작을 수행하는 코드 뭉치로,
    이 둘을 구분하는 유일한 차이는 독립성!

    함수는 자체적으로 독립적인 기능을 수행한다.
    메서드는 자신이 호출한 대상 객체에 관해서 동작을 수행

EX) 먼저 함수로서 호출, 메서드로서 호출

var 함수 = function(){
    console.log(this);
}

함수(); // 브라우저 : window / Node.js : global

var obj = {
    메서드: function(){
        console.log(this);
    }
};
obj.메서드(); // {메서드 : f}
  • 함수로서 호출을 한다면 this는 전역객체를 가리키게 된다.
  • 메서드로서 호출을 한다면 메서드를 호출한 주체 (메서드명 앞)

그래서 위 코드 처럼 저런 결과를 출력하게 된다.

여기서 헷갈리는 부분이 있다. 바로 함수로서 호출할 때 그 함수 내부에서의 this 관련한 것이다.

위에서 말했듯이 함수로 호출을 한다면 this에는 전역 객체를 가리킨다고 설명하였다.

var 객체1 = {
    바깥:function(){
        console.log(this); // (1)
        var 내부함수 = function(){
            console.log(this);  // (2) (3)
        }
        내부함수();

        var 객체2 = {
            내부메서드 : 내부함수
        };
        객체2.내부메서드();
    }
};
객체1.바깥();

실행 결과를 예측해보자.

(1) : 객체1
(2) : 전역객체
(3) : 객체2

말그대로

  • 함수로서 호출을 한다면 명시적으로 this를 바인딩 하지 않는 이상 this는 전역객체를 가리키게 되고,
  • 메서드로서 함수가 호출이 된다면 메서드를 호출한 주체가 this에 담기게 된다.

여기서 저렇게 내부함수에서 함수를 호출하더라도 this는 전역객체를 가리키게 된다는 점에서 설계상의 오류라는 말이 많다.

그래서 ES6부터 this 바인딩을 하지않는 arrow function 즉, 화살표 함수가 나오게 되었고 바로 위 컨텍스트에 있는 this를 그대로 가져다 사용한다.

저 위의 코드에서 내부함수라는 함수를 화살표 함수로 바꿔보자.

var 객체1 = {
    바깥:function(){
        console.log(this); // (1)
        var 내부함수 = () => {
            console.log(this); // (2)
        }
        내부함수();
    }
};
객체1.바깥();

출력 결과를 예상해보자.

  • (1)은 당연히 메서드로서 호출을 하였기 때문에 this는 객체1을 가리키게 된다.
  • (2)는 아까는 함수로서의 호출은 this가 전역 객체를 가리킨다고 하였지만 this바인딩 하지않는 화살표 함수는 바로 위 컨텍스트의 this를 참조하기 때문에 이것도 마찬가지로 객체 1을 가리키게 된다.

4. callback 함수로 호출 시

콜백함수란?

  • 함수 A의 제어권을 다른 함수 B에게 넘겨주는 경우 함수 A를 콜백 함수라고 한다.
  • 콜백 함수도 함수이기 때문에 기본적으로 this는 전역객체를 참조하게 되지만, 제어권을 받은 함수에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조하게 됩니다.

  • 제어권을 가진 함수가 콜백의 this를 지정해둔 경우도 있다.

setTimeout(function(){console.log(this)}, 1000); // (1)

document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a')
        .addEventListener('click', function(){
            console.log(this); // (2)
        }); 
  • (1) setTimeout 함수는 기본적으로 콜백 함수를 호출할 때 대상이 될 this를 지정하지 않는다. 따라서 콜백 함수 내부에서의 this는 전역객체를 참조하게 됩니다.

  • (2) addEventListener 메서드는 콜백 함수를 호출 할 때 자신의 this를 상속하도록 정의 되어 있기 때문에 앞서 지정한 엘리먼트가 this가 됩니다.

  • 하지만 이러한 경우에서도 개발자가 this를 바인딩해서 콜백을 넘기면 그에 따른다.

5. 생성자 함수 내부에서의 this

생성자 함수란?

  • 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수입니다.

그럼 생성자 함수로 호출 시에는 this는 어떤것을 참조하게되는가?

  • 새로 만들 인스턴스 객체 그 자체가 곧 this가 된다.
var Dog = function(name, type){
    this.bark = "멍멍";
    this.name = name;
    this.type = type
};

var 누렁이 = new Dog("누렁이", "진돗개");
var 뽀삐 = new Dog("뽀삐", "말티즈");

console.log(누렁이); // Dog { bark: '멍멍', name: '누렁이', type: '진돗개' }
console.log(뽀삐);  // Dog { bark: '멍멍', name: '뽀삐', type: '말티즈' }
  • Dog 라는 변수에 익명 함수를 할당하고 이 함수 내부에서 this 접근한다음 프로피트에 각각의 값을 대입합니다.

  • 여기서 this는 생성자 함수로 생성되는 누렁이 인스턴스, 뽀삐 인스턴스를 가리키는 것을 확인할 수 있습니다.

마치며

명시적으로 바인딩을 하지 않는 이상 5가지는 성립합니다.

  • 전역공간에서 this는 전역객체이다.
  • 함수로서 호출한 경우 this는 전역객체이다.
  • 메서드로 호출한 경우 this는 메서드를 호출한 주체이다.
  • 콜백 함수 내부에서의 this는 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따르며, 정의하지 않은 경우에는 전역 객체를 참조
  • 생성자 함수에서의 this는 생성될 인스턴스를 참조합니다.

여기서 한가지 의문점이 든다. 그럼 명시적으로 this를 바인딩을 하지 않는? 이말인 즉슨 명시적으로 this를 정할 수 있다라는 것이다. 이 주제에 대해서는 다음번에 알아보도록 하겠다.

참고 :
1. 코어자바스크립트
2. 인프런 - 코어자바스크립트

좋은 웹페이지 즐겨찾기