[JS] This란?

강풍윤·2022년 10월 25일
2

1. This 정의

this는 자신이 속한 객체 또는 자신이 생성할 인스턴스를 가리키는 자기 참조 변수입니다.

2. This 특징

  • 자신이 속한 객체나 자신이 생성할 인스턴스의 프로퍼티나 메서드를 멤버 참조 연산자(dot 연산자)로 참조할 수 있습니다.(예: this.name)
function Hi(name){
	this.name = name;
    console.log(`hi, ${this.name}`);
}
  • this 바인딩이란 this와 this가 가리키는 것을 연결하는 방법을 의미하며, this 바인딩은 함수 호출 방식에 의해 동적으로 결정합니다.

3. This 바인딩

호출 방식설명
(1) 일반 함수 호출- 기본적으로 this에는 전역객체(window, global)가 바인딩
- 일반함수로 호출된 모든 함수(중첩함수, 콜백함수 포함) 내부의 this에는 전역객체 바인딩
(2) 메서드 호출- 메서드는 메서드를 호출한 객체가 바인딩
(3) 생성자 호출- 생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩
(4) 명시적 바인딩- Apply, call, bind
- 화살표 함수(화살표함수 내의 this는 상위스코프의 this를 가리킴)

3-1) 일반 함수 호출

기본적으로 this에는 전역객체(window, global)가 바인딩됩니다.

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

a(); // Window

함수 안의 함수(중복함수)이지만 실행시키는 주체가 없기 때문에 dept가 아무리 깊어져도 전역객체인 window를 호출하게 됩니다.

function a(){
    console.log(this);
    function b() {
        console.log(this);
    }
    b(); // Window , 함수 안의 함수이지만 실행시키는 주체가 없어 dept가 아무리 깊어져도 window를 호출한다.
}
a(); // Window

3-2) 메서드 호출

메서드는 메서드를 호출한 객체가 바인딩됩니다.

let test = {
    one: 1,
    two: 2,
    three: function(){
        console.log(this);
    }
}

test.three(); // {one: 1, two: 2, three: ƒ}
// three는 일반함수이지만, three 안의 this를 test.three처럼 메서드 형식으로 호출했다면 three 안의 this는 three라는 메서드를 호출한 test를 객체로 바인딩

멤버 참조 연산자

여기서 test는 객체로써, 객체 안에 있는 함수를 멤버로 참조하는, 즉, test.three()와 같이 멤버 참조 연산자를 통해 메서드로 사용이 가능하다.

3-3) 생성자 호출

생성자 함수 내부의 this에는 생성자 함수가 생성할 인스턴스가 바인딩됩니다.

function Hi(name){
	this.name = name;
    console.log(`hi, ${this.name}`);
}

const c = new Hi("Danny");

c; // Hi {name: 'Danny'} 
// c라는 식별자로 생성자 함수 Hi를 호출했다면, 생성자 함수 Hi안의 this는 Danny라는 인수를 받는 생성장 Hi의 인스턴스를 바인딩

3-4) 명시적 바인딩이 필요한 이유

const theater = {
    place: '강남지점',
    movies: ['어벤져스', '태극기 휘날리며', '어바웃타임'],
    showMovieList() {
    	this.movies.forEach(function(movie) {
        	console.log(this.place, movie);
        });
    }
}

theater.showMovieList();
// undefined '어벤져스'
// undefined '태극기 휘날리며'
// undefined '어바웃타임'

// theater.showMovieList()로 메서드 호출을 하고 있기 때문에 당연히 showMovieList 안의 this는 메서드를 호출한 theater라고 할 수 있지만, 이 경우에선 다르다.
// forEach문 안에 함수는 콜백함수로, 이 경우는 메서드에게 전달한 콜백함수에서 호출된 this는 전역객체, 즉 Window를 의미하고, 전역객체에서 place라는 식별자를 선언하지 않았기 때문에 undefined를 호출하게 된다. 이를 해결하기 위해서는 명시적 바인딩을 통해 해결해주어야 한다.

메서드 내에 정의한 중첩 함수 또는 메서드에게 전달한 콜백 함수는 전역 객체가 바인딩되는 경우나 한 객체의 this를 통해 다른 스코프의 객체를 바인딩하고 싶은 경우 문제가 발생할 수 있습니다.

이러한 문제를, apply, call, bind 메서드나 화살표함수를 사용한다면 자신이 바인딩하고자 하는 대상을 보다 명시적으로 표현해줄 수 있습니다.

4. 명시적 바인딩 방법 (1) apply, call, bind

  • apply, call, bind 모두 Function.prototype의 메서드입니다. 모든 함수가 상속받기 때문에 함수에서 사용이 가능합니다.
  • apply와 call 메서드의 본질적인 기능은 함수를 호출하는 것입니다. call 메서드는 인수를 기본 형태로 받지만, apply는 인수를 배열형태로 받아 같은 결과를 호출할 수 있습니다. 이때, 배열은 유사배열도 해당합니다.
  • bind 메서드는 apply와 call 메서드와 달리 함수를 호출하지 않고 this로 사용할 객체만 전달합니다. 함수를 호출하려면 가리키는 대상을 더 명시적으로 표현하여 호출할 수 있습니다.

4-1) apply

function printThis(arg1, arg2) {
    console.log(this); // 일반 함수로 호출되면 this는 window를 가리켜야 한다.
    console.log(`apply를 통해 넘겨 받은 인자들: ${arg1}, ${arg2}`);
}

// this로 사용할 객체
const setThisValue = {영화제목: '어벤져스'};

printThis.apply(setThisValue, ["첫 인수", "둘째 인수"]);
// {영화제목: '어벤져스'}
// apply를 통해 넘겨 받은 인자들: 첫 인수, 둘째 인수

// apply 메서드는 함수 호출된 값을 전달

4-2) call

function printThis(arg1, arg2) {
    console.log(this); // 일반 함수로 호출되면 this는 window를 가리켜야 한다.
    console.log(`call을 통해 넘겨 받은 인자들: ${arg1}, ${arg2}`);
}

// this로 사용할 객체
const setThisValue = {영화제목: '어벤져스'};

printThis.call(setThisValue, "첫 인수", "둘째 인수");
// {영화제목: '어벤져스'}
// call을 통해 넘겨 받은 인자들: 첫 인수, 둘째 인수

// call 메서드는 함수 호출된 값을 전달

4-3) bind

function printThis(arg1, arg2) {
    console.log(this); // 일반 함수로 호출되면 this는 window를 가리켜야 한다.
    console.log(`bind를 통해 넘겨 받은 인자들: ${arg1}, ${arg2}`);
}

// this로 사용할 객체
const setThisValue = {영화제목: '어벤져스'};

printThis.bind(setThisValue);
// f printThis(arg1, arg2){...} 
// bind는 객체만 전달(함수 호출을 하려면 더 명시적으로 표현해야 한다)

printThis.bind(setThisValue)();
// {영화제목: '어벤져스'}
// bind를통해 넘겨 받은 인자들: undefined, undefined

printThis.bind(setThisValue, "첫 인수", "둘째 인수")();
// {영화제목: '어벤져스'}
// bind를 통해 넘겨 받은 인자들: 첫 인수, 둘째 인수

5. 명시적 바인딩 방법 (2) 화살표 함수

  • 화살표 함수는 함수 자체의 this 바인딩을 갖지 않습니다. 따라서 화살표 함수 내부에서 this를 참조하면 상위 스코프의 this를 그대로 참조하게 됩니다. 이를 lexcial this라고 합니다.
  • 화살표 함수 내부에서 this를 참조하면 일반적인 식별자처럼 스코프 체인을 통해 상위 스코프에서 this를 탐색합니다.
// var를 통해 변수를 선언하면 전역객체 window의 프로퍼티로 저장된다.
var movieTitle = "어벤져스";
const obj = { movieTitle: "태극기 휘날리며" };

function nestedFunc() {
    function normalFunc() {
        console.log(this.movieTitle);
    }
    const arrowFunc = () => {
        console.log(this.movieTitle);
    };
    const nestedArrowFunc = () => {
        const arrowFunc2 = () => {
            console.log(this.movieTitle);
        };
        arrowFunc2();
    };
    // 여기서 this는 일반함수 호출로 전역객체의 movieTitle인 "어벤져스"
    normalFunc(); // "어벤져스"
    // 여기서 this는 화살표함수 호출로 이를 감싸는 nestedFunc가 call로 명시한 obj를 this 값으로 가리킵니다.
    arrowFunc(); // "태극기 휘날리며"
    // arrowFunc2는 상위 스코프가 nestedArrowFunc 즉 화살표 함수이므로 한 단계 더 상위로 올라가 nestedFunc의 this 값인 obj를 this 값으로 가리킵니다.
    nestedArrowFunc(); // "태극기 휘날리며"
}

// call 메서드를 통해서 nestedFunc의 this 값을 obj로 명시해준다.
nestedFunc.call(obj);
const d = () => console.log(this);
d(); // Window

참고자료

  • 코어 자바스크립트 this
  • 알아서 잘 딱 깔끔하고 센스있게 정리하는 JavaScript 핵심개념
  • MDN문서 JavaScript this
profile
https://github.com/KANGPUNGYUN

1개의 댓글

comment-user-thumbnail
2022년 11월 1일

갓풍윤

답글 달기