JavaScript

[javascript] this -1

  • -
반응형

이번 포스트에서는 알쏭 달쏭 헤깔리는 javascript에서의 this에 대해서 살펴보자.

 

javascript에서의 this

javascript에서의 this가 어려운 이유는 그때 그때 달라지기 때문이다.

다른 언어에서의 this와 그 정의 부터 남다른데 java에서의 this가 현재 실행되고 있는 객체고정된 반면 javascript에서의 this는 함수를 호출하는 녀석에 따라 달라진다. 즉 실행 문맥(execution context)에 따라 달라진다.

일반적인 this의 구분해 놔야 할 this

this를 사용할 때는 대부분 아래의 4가지 상황에 대해 정확히 이해하고 사용해야 한다.

  1. 전역 객체에서의 this:  
  2. 객체의 메서드에서 this
  3. apply/bind/call 함수에서의 this
  4. 생성자 함수에서 this

특히 1, 2, 3 상황에서 this 동작을 주의해서 살펴보자.

호출 방식에 따른 this의 변화

간단한 javascript 코드를 만들고 this를 살펴보자.

let dog = {
  species: "보더콜리",
  eat() {
    console.log(`${this.species}는 잘 먹는다`, this); // 이 this의 정체를 알 수 있는 사람은 없다.
  },
};

dog.eat();

dog 안에는 specises와 eat가 선언되어있고 eat에서는 this를 활용하고 있는 너무 간단한 코드이다. java 코드에 익숙한 친구들은 이 시점에서 this를 추정하려고 한다. 당연히 dog라는 객체일 것이다. 하지만 여기서 주의할 점은 javascript에서의 this는 선언 시점에 결정되지 않는다는 점이다.

일단 dog.eat()의 결과는 아마도 여러분의 예상대로 잘 출력될 것이다. 처음 생각대로 this는 dog라는 착각에 빠지기 참 쉽다.

여기서 위와 같이 출력될 수 있었던 이유는 eat 라는 함수를 "dog"를 통해서 실행했기 때문이다. 즉 실행 시점에서 this가 dog로 결정되는 것이다. 

약간 아리송할 때 다음의 코드를 살펴보자.  이제 dog.eat라는 함수를 전역 레벨의 변수에 할당시켜 두고 호출해본다.

let eatFun = dog.eat;
eatFun();

eatFun이 전역 레벨이기 때문에 마지막 줄은 window.eatFun()과 같다. (물론 let 변수는 windowr객체에 binding 되지 않기 때문에 window.eatFun()은 오류이다. 말이 그렇다는 것이다.)즉 이제는 window를 통해서 실행시키는 것이다. 출력 결과는 아래와 같다.

즉 이번에는 this가 window로 binding 되었기 때문에 this.species는 window.species가 되었고 당연히 undefined인 것이다.

이처럼 this는 동일한 함수라고 하더라도 누구를 통해서 호출되느냐(execution context)에 따라서 정체성이 바뀌어버리는 고약한 녀석이다.

 

this의 고정

그럼 this를 확인하려면 누구를 통해서 호출하는지만 알면 되니까 그래도 좀 쉽게 파악할 수 있지 않을까? 생각할 수 있지만 this는 apply/bind/call과 같은 함수를 통해서 고정할 수도 있다. 다음 코드 처럼 eatFun의 bind를 이용해 dog를 this로 binding 시켜보자.

let bindToDog = dog.eat.bind(dog);
bindToDog();

이제는 분명 window를 통해서 실행하고 있음에도 불구하고 출력 결과 this는 dog이다.

물론 전혀 새로운 객체를 만들어서 binding 해도 잘 동작할 것이다.

let cat = {
  species: "스핑크스",
};

let bindToCat = dog.eat.bind(cat);

// 아래 함수만 가지고 this를 추정할 수 있을까?
bindToCat();

여전히 하나의 함수인 dog.eat를 사용하고 있지만 이제 cat까지 잘 먹일 수 있게 되었다.

 

콜백함수에서의 this

콜백 함수는 어떤 일을 처리할 때 전달된 함수(콜백 함수)를 이용해서 처리하기 위한 함수이다. 

예를 들어 배열을 정렬할 때 사용되는 sort 함수는 파라미터로 compareFn 타입의 콜백함수를 받는다. 그럼 그 내부에서 사용되는 this는 누구일까?

콜백 함수는 전역함수로 실행되기 때문에 기본적으로 콜백 내부의 this는 일반적으로 window가 된다. 전역 객체가 정렬 기능을 수행하면서 콜백을 불러주기 때문이다.

let arr = [1, 2, 3];
arr.sort(function (a, b) {
    console.log("sort의 this", this); // this는 window
    return a - b;
});


let timer = {
    alarm: function (timeout) {
        console.log("alarm function 의 this", this);
        setTimeout(function () {
            console.log("settimeout의 this", this);
        }, timeout * 1000);
    },
};
timer.alarm(4);

 

하지만 모든 콜백에서 this가 window는 아니다. 예를 들어 이벤트 콜백은 이벤트 소스에 추가해준다. 따라서 event callback 내에서의 this는 이벤트 소스가 된다.

document.querySelector("#click").addEventListener("click", function (e) {
    console.log(this);
    console.log(e.currentTarget);
});

참고로 event source는 이벤트 객체가 가지는 currentTarget 속성으로도 얻을 수 있다.

event callback에서의 this는 event source

 

생성자 함수에서의 this

다음은 생성자 함수에서 this에 대해 알아보자. 생성자 함수에서의 this는 함수를 통해서 생성된 객체를 나타낸다.

function Calculator() {
    this.count = 20;

    this.plus = function () {
        this.count++;
        console.log(`count: ${this.count}`, this);
    };
}

let myCalc = new Calculator();
myCalc.plus();

 

Calculator를 생성자 함수로 만든 myCalc를 이용해서 plus()를 호출해보면 그때의 this는 myCalc가 된다. 따라서 plus도 정상적으로 동작하고 마지막에 출력한 myCalc.count는 21이 된다.

 

헤깔리는 this의 최대 대책

this와 관련된 문제는 bindToCat()이라는 함수만 가지고 this를 추정할 수 있을까? 하는 항목이다. 어떤 함수를 전달받았을 때 그 함수가 binding 된 함수인지 그냥 함수인지 사용할 때는 알 수가 없다.

콜백 함수 중 event callback에서 this가 window가 아닌 event source라고 쉽게 생각할 수 있을까? 콜백이 최종적으로 어디에 등록되었는지 소스코드를 확인하지 못한 상황에서 이를 추정하기도 쉽지 않다.

따라서 개인적으로 this를 사용하는 가장 확실한 방법중 하나는 잘 모르겠을 때 꼭 콘솔에 this를 출력해보고 이녀석이 지금 누구인지 확인하고 사용하자라는 것이다.(너무 성의 없나 ㅜㅜ)

 

 

 

[javascript] this - 2

arrow 함수와 this 앞선 포스트에서 꾸역 꾸역 execution context와 this에 대해서 알아갈 무렵 ECMA6가 발표되면서 arrow 함수라는 것이 발표되고 또다시 this는 혼돈에 빠지게 된다. https://goodteacher.tistory.com/

goodteacher.tistory.com

 

반응형

'JavaScript' 카테고리의 다른 글

[ajax]1. 개요  (0) 2022.03.14
[javascript]cors 크롬 플러그인 사용  (0) 2022.03.11
[새로운 기능] Template Literal  (0) 2021.11.20
[새로운 기능] let과 const  (0) 2021.11.20
[javaScript]Clipboard 활용  (0) 2021.09.20
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.