책 읽다가 코딩하다 죽을래

자바스크립트 this. 이것은 무엇인가 본문

코딩/자바스크립트

자바스크립트 this. 이것은 무엇인가

ABlue 2021. 9. 17. 20:21

 

📚 this(자기 참조)란?

 

this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수 입니다. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메소드를 참조할 수 있습니다.

<모던 자바스크립트>에서 이웅모 저

 

 

 

즉 자바스크립트 내에서 this는 '누가 나를 불렀느냐'를 뜻한다고 합니다.

 

그래서 this를 소환했을 때의 어떤 실행 컨텍스트에서 불러내었는지를 알면 이해하기 쉬워집니다.

 

📋 실행 컨텍스트에 따라 달라지는 this

 

this는 다음 실행 컨텍스트에 따라 달라집니다.

 

전역 공간에서 -> window/global

함수 내부에서 -> window/global

메소드 호출 시 -> 메소드 호출 주체

callback함수에서 -> 함수 내부에서와 동일

생성자 함수에서 -> 인스턴스

 

☝ 전역공간에서의 this

 

console.log(this);

 

전역 공간에서 this를 출력한다면 다음과 같습니다.

자바스크립트를 사용하여 브라우저 콘솔창으로 확인했다면 window가 나오고 Node.js를 통하여 출력했을 경우 global 객체가 나올 것입니다.

this가 전역 공간에서 호출하게 되면 this를 호출한 객체는 window/global이 되니 이는 당연한 얘기입니다.

 

 

☝ 함수 내부에서의 this

 

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

 

 함수 내부에서 this를 출력하면 window/global 객체가 출력됩니다.

 


 

function b(){
    function c(){
        console.log(this);
    }
    c();
}
b();

 

이렇게 함수를 2번 감싸도 여전히 window/global 객체가 출력됩니다.

 

 


 

const d = {
    e : function(){
        function f(){
            console.log(this);      //window/global 객체가 나온다.
        }
        f();
    }
}
d.e();

 

이런식으로 변수 안에 함수에서 불러와도 window/global 객체가 출력됩니다.

자바스크립트 함수에서는 기본적으로 this는 window/global 객체를 가리킵니다.

기본적이라는 건 예외상황이 있다는 것이고, 예외는 뒷부분에서 살펴보겠습니다.

 

 

+ this에 따른 react, react hooks 문법 쉽게 외우는 방법[더보기 클릭]

더보기

이 부분은 js 웹 프레임워크인 react에 관련된 내용입니다.

react와 무관한 사람이라면 그냥 지나치셔도 됩니다.

 

react를 처음 배웠을 때

클래스형 컴포넌트인 react와

함수형 컴포넌트인 react hooks를 외울 때 되게 혼란스러워했을 겁니다.

 

하지만 앞에서 배운 함수 내부에서의 this를 잘 아셨더라면 앞으로 헷갈릴 일은 없을 겁니다.

 

 다음은 input 태그에 입력한 값을 data로 저장해 출력하는 간단한 예제입니다

위쪽 코드는 react 클래스 문법이며 아래는 react hooks 함수형 문법입니다.

코드를 전부 이해하실 필요 없으며 그냥 훑어보시고 아래로 내려가 보시길 바랍니다.

 

클래스 컴포넌트

 

const React = require('react');
const { Component } = React;

class App extends Component{
    state = {
	data:'',
        value:'',
    };

    onSubmitForm = (e) => {
        e.preventDefault();
        this.input.focus();
        this.setState({
            data: this.state.value,
            value: '',
        });
    };

    onChangeInput = (e) => {
        this.setState({value : e.target.value});
    };

    onRefInput = (e) => {
        this.input = e;
    };
    render() {
        return (
            <>
                <form onSubmit={this.onSubmitForm}>
                    <input ref={this.onRefInput} value={this.state.value} onChange={this.onChangeInput}/>
                    <button>입력</button>
                </form>
                <div>{this.state.data}</div>
            </>
        )
    }
}

module.exports = App;

 

함수 컴포넌트

 

const React = require('react');
const { useState, useRef } = React;

const App = () => {
    const [data, setData] = useState('');
    const [value, setValue] = useState('');
    const inputRef = useRef(null);

    const onSubmitForm = () => {
        e.preventDefault();
        inputRef.current.focus();
        setData(value);
        setValue('');
    };

    const onChangeInput = (e) => {
        setValue(e.target.value);
    };

    return (
        <>
            <form onSubmit={onSubmitForm}>
                <label htmlFor="wordInput">글자를 입력하세요.</label>
                <input ref={inputRef} value={value} onChange={onChangeInput}/>
                <button>입력</button>
            </form>
            <div>{data}</div>
        </>
    );
};

module.exports = App;

 

잘 보시면 클래스 컴포넌트는 this를 많이 활용합니다.

왜냐하면 클래스 내에 state와 이벤트 함수에 접근하려면 this를 사용해야 하기 때문이죠

클래스 내에 this는 클래스 즉 인스턴스를 가리킵니다.

그래서 this 없이는 각 인스턴스마다 state, 이벤트 함수에 접근하기 어렵습니다.

 

 

하지만 함수 컴포넌트는 다릅니다.

아까 위에서 함수 내에서의 this는 window/global 객체를 가리킨다고 했죠?

react hooks도 이와 같습니다.

hooks 내에서 this를 사용한다면 해당 함수가 아닌 window/global객체를 가리키기 때문에 this를 사용할 수 없습니다. 

 

그래서 이렇게 외우시면 됩니다.

클래스 컴포넌트는 this를 사용하고 함수 컴포넌트는 this를 사용하면 안 된다.

그러면 react, react hooks의 문법의 절반은 외우신 겁니다!

 

 

☝ 메소드 호출 시에 this

 

메소드 호출 시에는 불러온 객체를 살펴보면 됩니다.

 

const g = {
    h: function(){
        console.log(this);      //객체 g의 h값이 나온다.
    }
}
g.h();

 

출력 결과

 

h 함수를 불러온 즉 g.h()에서의 . 앞인 g 객체가 this를 불러온 것이니 this는 g를 가리킵니다.

 


 

const i = {
    j: {
        k: function(){
            console.log(this);      //객체 i의 j의 k값이 나온다.
        }
    }
}
i.j.k();        //함수를 호출한 대상이 i.j이므로 k가 this가 된다.

 

k함수를 불러온 즉 i.j.k()에서의 . 앞인 i.j 객체가 this를 불러온 것이니 this는 i.j를 가리킵니다.

 

그래서 this는 자신을 불러온 객체를 나타내고

함수는 전역 객체의 메소드 라고 생각하시면 이해하기 쉽습니다.

 

물론 이 말이 맞다는 게 아니지만 더욱 외우기 수월해진다는 것입니다.

this를 불러온 객체가 분명하다면 그 객체를 가리키는 것이고

없다면 그냥 window/global을 가리킨다고 생각하면 되니까요.

 

 

☝ callback 함수에서의 this

 

callback함수에서는 apply, bind, call 함수를 설명하겠습니다.

이 함수에 대해 잘 모르신다면 제로초님의 블로그(함수의 메소드와 argument)[클릭] 를 보고 오셨으면 합니다.

 

 

const callback =  function(){
    console.dir(this);
};

const obj1 = {
    p: 1,
    q: function(cb){
        cb();       //window 객체가 나온다.
    }
};
obj1.q(callback);

 

콜백 함수에서의 this는 기본적으로 함수내부에서의 this와 동일합니다.

 

그런데 콜백함수에서의 제어권을 넘겨받은 다른 함수에서 this를 명시하게 되면 얘기가 달라집니다.

 

 

const callback =  function(){
    console.dir(this);
};

const obj1 = {
    p: 1,
    q: function(cb){
        cb.call(this);       //this가 obj1을 가리키게 만든 후 그 인자를 콜백함수에 주었다.
    }
};
obj1.q(callback);

 

cb함수의 this를 call함수를 통해 this가 obj1을 가리키라고 명시하게 되었습니다.

그러면 obj1이 this라고 명시받은 callback함수의 this도 obj1이 됩니다. 

 


 

var obj2 ={
    r: 1
};
setTimeout(callback, 100);       //this는 window/global이 된다.

 

setTimeout도 마찬가지입니다.

this를 명시해두지 않으면 자동적으로 callback함수는 전역 객체인 window/global을 가리키지만

 

 

 

var obj2 ={
    r: 1
};
setTimeout(callback.bind(obj2), 100);       //this는 obj2가 된다.

 

 

이런 식으로 bind함수를 통해 this가 obj2로 명시해두면 callback함수에서의 this도 obj2가 됩니다.

 


 

document.body.innerHTML += '<div id="a">클릭하세요</div>';

document.getElementById('a').addEventListener('click',function(){
    console.dir(this);
});

 

이런 원리를 알게 되면 우리가 이벤트 함수를 만들 때 이벤트 함수 내부에서의 this가 window/global이 아닌 이벤트를 일으킨 dom 객체인지 알게 됩니다.

 

document.getElementById('a').addEventListener(...

우리가 이미 앞에서 이 함수를 불러오는 객체가 a태그라고 명시해두었기 때문입니다.

 

document.body.innerHTML += '<div id="a">클릭하세요</div>';

document.getElementById('a').addEventListener('click',function(){
    console.dir(this);
}.bind(obj2));       //기본적으로는 a 태그 dom 객체가 나오지만 이렇게 bind함수를 쓰면은 obj2가 this가 된다.

 

하지만 bind함수로 this가 obj2라고 명시하게 되면 이벤트 함수 내에서의 this도 obj2가 됩니다.

 

☝ callback 함수에서의 this 정리

 

 

 

1. 기본적으로 함수의 this와 같습니다.

 

2. 제어권을 가진 함수가 callback의 this를 명시한 경우 그에 따릅니다.

 

3. 개발자가 (apply, bind, call을 통하여)this를 바인딩한 채로 callback을 넘기면 그에 따릅니다.

 

 

 

☝ 생성자 함수에서의 this

 

생성자 함수에서의 this는 간단합니다.

 

function Person(n, a) {
    this.name = n;
    this.age = a;
}
const ablue = new Person('ablue', 25);
console.log(ablue);

 

생성자 함수는 해당 인스턴스를 가리키게 됩니다.

this.name의 this는 Person을 가리킵니다.

 

 


📖 정리

 

 

자바스크립트에서의 this는 다른 언어들의 this보다도 많은 차이가 있습니다.

그래서 면접 질문으로 많이 나오기도 하며 그만큼 중요한 개념입니다.

하지만 5가지 상황에서의 this의 차이를 알게 되면 크게 문제 될 일은 없으니 이 점만 똑똑히 외워둡시다.

 

전역 공간에서 -> window/global

함수 내부에서 -> window/global

메소드 호출시 -> 메소드 호출 주체

callback함수에서 -> 함수 내부에서와 동일

생성자 함수에서 -> 인스턴스