article thumbnail image
Published 2022. 11. 2. 11:44

js에서 this란?

일반적으로 js에서의 this는 다른 언어들과는 조금 다르다(C++, Java 등..) 브라우저 개발자 도구에서 콘솔로그로 this를 찍어보면 기본적으로 window 객체가 나온다. 함수에서 this를 호출해도 window지만 nodejs환경에서 this는 global 객체로 나온다.

하지만 최근에는 global this로 합쳐졌다고 한다 + strict모드 상에서는 this를 찍어보면 undefined가 된다. 이러한 부분은 단순히 console.log만 찍어봐도 알 수 있다(참고).

 

이렇게 나오는 이유

일반적으로 this는 호출될 때 결정된다. 현재 브라우저상에서 콘솔 로그를 찍어보면 전역 범위에서 호출되는데 이때 this는 브라우저 최상단에 window라는 전역객체를 가리키고 있어 window가 출력된다.

 

this가 바뀌는 경우

아래 sayname이라는 함수가 호출될 때의 2가지 경우가 있다.

obj의 메소드 sayname()을 실행한 것과 sayname의 선언을 전달해주고 실행시킨 것이 있는데 둘의 결과는 다르다

 

왜 다르냐 하면 함수가 호출될 때 this가 결정된다고 했는데 obj.sayname()은 함수 앞에 객체가 붙어 호출되므로 this가 obj라는 객체가 된다. 하지만 sayN은 호출될때 연관된 객체가 없으니 window객체가 되어 window.name으로 빈값이 출력된다

const obj = {
    name: "name",
    sayname() {
        console.log(this.name)
    }
}

obj.sayname()

const sayN = obj.sayname
sayN()

아래 코드는 생성자 함수로써 this를 자기 자신으로 만든다

function Human(name) {
    this.name = name
}

new Human('name')

 

this가 바뀌는 경우 2가지

  1. 앞에 객체가 붙는 경우
  2. new 객체를 만드는 경우

 

this를 직접적으로 바꿔주는 것들 bind, call, apply

function sayName() {
    console.log(this.name)
}

sayName(); //window.name

sasyName.bind({name: "홍길동"})() // this.name은 홍길동

sasyName.apply({name: "홍길동"}) // this.name은 홍길동

sasyName.call({name: "홍길동"}) // this.name은 홍길동

세가지는 사실 비슷한데

 

bind는 this란 값만 바꿔서 새로운 함수를 만들어주는 역할을 한다. 따라서 실행은 별도로 해줘야함

apply는 this란 값만 바꿔서 새로운 함수를 만들어 실행까지 시켜준다

call도 this란 값만 바꿔서 새로운 함수를 만들어 실행까지 시켜준다

 

apply와 call의 차이점

function add(a,b) {
    return a + b
}

add.apply(null,[3,5]) //this안쓰면 Null넣어도 상관없다
add.call(null,3,5) //this안쓰면 Null넣어도 상관없다

apply는 매개변수를 배열로 넣어준다.

 

call은 매개변수를 변수에 맞춰 값으로 넣어준다.

 

주의해야할 점 - 화살표함수

const obj = {
    name: "name",
    sayName: () => {
        console.log(this.name)
    }
}

obj.sayName()

sayName이 화살표함수로 바뀌었는데 화살표함수의 this는 또 다르다. 화살표 함수는 부모의 스코프를 따라간다. 스코프는 함수의 선언과 관련되어 있어 window와 관련되어진다.

 

const obj = {
    name: "name",
    sayName() {
        function inner() {
            console.log(this.name)
        }
        inner()
    }
}

obj.sayName()

이때 inner의 this는 호출을 기준으로 this가 바뀌는 경우와 전혀 연관되어 있지 않아 inner 실행컨텍스트 상의 this로 가게된다. 이때 this는 window (this를 바꿔주는 것과 아무런 연관이 없다)

 

따라서 inner는 window.name이 되어 빈 값을 출력한다.

 

 

하지만 또 화살표함수에 예외가 발생한다.

const obj = {
    name: "name",
    sayName() {
        const inner = () => {
            console.log(this.name)
        }
    }
}

obj.sayName()

이 경우를 분석하려면 다시 호출부터 분석해보면 된다. 화살표 함수 inner는 호출을 기준으로 만들어지는 this가 아닌 부모의 스코프를 따라가기 때문에 부모의 실행컨텍스트 속 this를 받아와 sayName의 this.Name을 출력하게 된다.

 

따라서 주의해야할 점은 객체가 누구냐이고 이 객체 그러니까 this의 주인은 호출될 때 결정된다는 것이다(선언 아님, 화살표함수 예외)

그렇기때문에 단순히 메소드, 함수의 선언만으로는 this가 누군지 전혀 알 수 없다

 

함수가 호출될 때 this 값을 결정해주기 때문에 호출 스택에 this가 누군지 같이 적으면서 생각하면 코드 분석할 때 매우 쉬워진다.

 

this를 분석할 수 없는 케이스

이제 많이 헷갈리는 부분들이 있다

const header = document.querySelector('.header')
header.addEventListner('click', function() {
    console.log(this);
}

위 코드에서 this를 찾아보면 딱히 호출되는 부분도 없고 익명함수로 전달되서 this가 window처럼 보인다.

하지만 여기서 this는 header라는 클래스를 가지는 요소가 선택된다.

 

사실 위 코드는 정상적으로 분석할 순 없다. 왜냐하면 addEventListner가 어떻게 동작하는지, 실제 코드가 어떻게 되는지 확인할 수 없다는 것과 this는 호출될 때 결정되기 때문이다. 단순히 addEventListner함수만 호출됐을 뿐이라 그 함수만 보고는 파악할 수 없다.

 

호출은 addEventListner 어딘가에서 실행되기 때문에 저렇게 생긴 여러 함수들이 어떤 this를 가지는지 외워야하는 부분이 있다.

코드를 분석해서 정확히 알 수 있는게 아니다.

 

const header = document.querySelector('.header')
header.addEventListner('click',() => {
    console.log(this);
}

만약 화살표 함수로 바꾸면 어떻게 될까?

화살표 함수는 bind, apply, call같은 this를 직접적으로 바꿔주는 작업을 수행할 수 없다.

따라서 부모를 따라간다 화살표 함수의 부모는 보이지 않기때문에 window가 된다

 

 

this - JavaScript | MDN

JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작합니다. 또한 엄격 모드와 비엄격 모드에서도 일부 차이가 있습니다.

developer.mozilla.org

https://www.youtube.com/watch?v=pgo6URFz8tc&list=PLcqDmjxt30Rt9wmSlw1u6sBYr-aZmpNB3&index=6 

https://www.youtube.com/watch?v=3l2yiP0ch3U&list=PLcqDmjxt30Rt9wmSlw1u6sBYr-aZmpNB3&index=7 

 

'TIL > 개념정리' 카테고리의 다른 글

WebPack을 사용하는 이유와 정리  (0) 2023.01.07
SQL Injection이란?  (0) 2022.11.13
호이스팅이란?  (0) 2022.10.31
실행 컨텍스트란?  (0) 2022.10.31
스코프 체인  (0) 2022.10.27
복사했습니다!