본문 바로가기
Javascript

[Javascript] this: 이름 값 못하는 자바스크립트의 this

by 슥짱 2022. 7. 3.

자바스크립트에서 this는 다른 언어에서와는 다르게 동작한다. Java와 같은 언어의 경우, 인스턴스가 생성되면 this는 항상 해당 객체를 가르키지만, 자바스크립트의 this는 주로 함수의 호출 시점에 결정되기 때문에, "이것" 이라는 이름 값을 하지 못하고 그 대상이 자꾸만 바뀐다. 

전역 스코프에서의 this

먼저 전역 스코프에서 this는 전역 객체(globalThis)을 가르킨다. 브라우저는 Window, 노드에서는 Global 객체가 될 것이다.

// 브라우저
console.log(this) // Window

함수에서의 this

단순 함수 호출

단순 호출에서도 마찬가지로 this는 전역 객체를 가르킨다.

function nonStrict() {
    console.log(this);
}

nonStrict(); // Window

 

전역 스코프에서의 this와 한 가지 다른 점은, 전역 스코프에서는 엄격 모드의 여부와 상관 없이 항상 전역 객체를 가르키지만, 함수 호출에선 엄격 모드가 적용 됐을 때 그 값이 undefined가 된다.

function strict() {
    "use strict";
    console.log(this);
}

strict(); // undefined

메소드에서의 this

메소드에서는 this는 호출 주체(객체)를 가르킨다.

const person = {
    name: 'seukjjang',
    printThis() {
        console.log(this);
    }
};

person.printThis(); //{name: 'seukjjang', printThis: ƒ}

맨 처음 강조 했듯, this는 호출 시점에 결정된다는 점에 주의해야 한다. 위 예시에서 printThis()person의 메소드로서 호출 되었기 때문에 this는 호출 주체인 person을 가르킨다.

 

하지만 다음과 같이 메소드를 다른 변수에 담아 단독으로 호출하면 어떻게 될까?

const { printThis } = person;
printThis(); //Window

메서드가 아니게 된 `printThis()`의 this는 다시 전역 객체를 가르킨다.

 

반대로 다음과 같이 객체의 메소드를 나중에 부여하면 this는 새로운 객체를 가르킨다.

const person2 = {
    name: 'jaesoekjjang'
};

person2.printThis = printThis;
person2.printThis() //{name: 'jaesoekjjang', printThis: ƒ}

이제 함수의 "호출 시점"에 결정된다는 말이 이해될 것이다.

 

그럼 다음 코드는 어떤 결과를 출력할까?

const person3 = {
    name: 'jaesoek',
    say() {
        function printName() {
            console.log(this.name);
        }
        printName();
    }
}

person3.say();

this가 함수 호출 시점에 결정된다는 것을 생각하면 쉽게 undefined가 될 것임을 알 수 있다. say()person3의 메소드로 호출되고 있지만 실질적으로 this를 갖고 있는 함수인 printName()은 단순 함수로 호출 되었기 때문이다.

화살표 함수에서의 this

화살표 함수에서 this는 또 다르게 동작한다. 화살표 함수에서의 this는 함수의 호출 시점이 아닌 선언 시점에 결정된다. 화살표 함수는 자신만의 this를 갖지 않는데, 그 대신 클로저와 같이 자신의 상위 스코프에서 this를 검색해 나간다.

 

앞선 문제를 화살표 함수를 사용해 바꾸면

const person3 = {
    name: 'jaesoek',
    say() {
        const printName = () => {
            console.log(this.name);
        }
        printName();
    }
};

person3.say() //jaesoek

이번에는 "jaesoek"이라는 결과를 출력한다. printName()의 선언 시점에 this는 자신의 상위 스코프인 say()와 같은 this를 참조하게 됐고, say()의 호출 시점에 thisperson3였으니 결과적으로 printName()의 thisperson3를 가르키게 된 것이다.

this를 원하는 값으로 바꿀 수 있는 bind 메서드

`bind` 메소드(`Function`의 메소드이다)를 사용하면 함수 호출 시점에 상관 없이 원하는 객체로 this를 바인딩 할 수 있다. bind()는 첫 번째 인자로 this로 설정할 객체를 받고 결과로 바인딩 된 새로운 함수를 호출한다.

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

printName() // undefined
printName.bind(person)() //seukjjang

printName()thisWindow 객체이고, Window 객체는 name 속성을 갖지 않기 때문에 아무것도 출력되지 않는다. 반면 bind()가 반환한 함수의 thisperson 객체로 바인딩 되었기 때문에 객체의 이름이 출력되는 것을 확인할 수 있다. bind() 메소드를 화살표 함수에서 호출하는 경우 의도한 대로 동작하지 않을 것이다. 화살표 함수는 자신의 this가 없기 때문이다.

 

그럼 만약 화살표 함수의 부모 함수를 바인딩 하면 어떻게 될까? 이전 문제를 다시 가져와서 확인해보자.

const person3 = {
    name: 'jaesoek',
    say() {
        const printName = () => {
            console.log(this.name);
        }
        printName();
    }
};

const { say } = person3;
say() // undefined

앞서 봤듯 say()는 단순 함수로 호출했기 때문에 Window 객체를 가르키고, printName()도 같은 this를 참조해 undefined가 나온다.

 

그런데 여기서 bind()로 다시 person3를 바인딩 해주면?

say.bind(person3)(); // jaesoek

"jaesoek"을 출력하는 것을 확인할 수 있다. 화살표 함수의 this는 상위 스코프의 this를 "참조"하고 있기 때문에 부모 함수의 this가 다른 값으로 바인딩 되면 따라서 바뀌게 된다.

 


참고자료

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this

 

this - JavaScript | MDN

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

developer.mozilla.org

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/Arrow_functions

 

화살표 함수 - JavaScript | MDN

화살표 함수 표현(arrow function expression)은 전통적인 함수표현(function)의 간편한 대안입니다. 하지만, 화살표 함수는 몇 가지 제한점이 있고 모든 상황에 사용할 수는 없습니다.

developer.mozilla.org

 

댓글