본문으로 바로가기

this binding

category JavaScript/기초 2020. 9. 30. 03:44

this

자바스크립트에서는 함수를 호출할 때 기존 매개변수로 인자값과 (암묵적으로)this인자가 함수의 내부로 전달된다.

이 this인자는 기본적으로 자기 자신의 객체를 가르키는데 상황에 따라 조금씩 다르다

크게 5가지 상황으로 나누어 질 수 있으며 지금부터 알아보자

1. 전역 공간

전역 공간이라는 것은 어떤 객체에 속하지 않은 상태를 얘기하는 것

    console.log(this);

이 코드는 전역 공간에 속해있기 때문에 this가 전역 객체인 window를 가르킨다

??

맞다 객체에 속해있지 않다고 설명했었지만 사실 어디에도 속하지 않았다는 것은 전역 객체에 속해있다는 것을 의미한다

우리가 '자바스크립트 맨땅에 함수를 선언하고 변수를 선언하는 것은 곧 전역 객체 즉, window객체에 프로퍼티를 추가하는 것'이라고 생각하면 쉽다

    var wName = "my name is window"

    console.log(wName);
    console.log(this.wName);
    console.log(window.wName);

위 코드의 실행 결과

를 보면 이해가 쉬울 것이다

2. 함수 내부

    var wf = function f() {
           console.log(this);
    }

아까 한 말이 기억나는 가?

자바스크립트 맨땅에 함수를 선언하고 변수를 선언하는 것은 곧 전역 객체 즉, window객체에 프로퍼티를 추가하는 것 이라고 했다

그렇다면 wf는..?

그렇다 window객체에 속해있다고 생각하면 되기 때문에 wf함수 안의 this는 window객체를 가르킨다

    var wName = "my name is window";

    var wf = function f() {
        console.log(this.wName);
    }

    wf();

위 코드의 실행 결과

3. 메소드

    var a = {
        name : "my name is a",
        b: function(){---①
            console.log(this); 
        }
    } 
    a.b();

메소드란? 간단하게 객체가 가지고 있는 함수를 메소드라고 부른다 이 코드는 'b메소드를 가진 a객체'라고 설명할 수 있다

메소드에서는 메소드를 호출한 객체가 this가 된다

①번의 b메소드를 호출한 객체는 누구인가? 바로 a이다

위 코드에서의 this는 곧 a가 된다

    var tName = "my name is window";

    var a = {
        tName:"my name is a",
        b: function () {
            console.log(this.tName);
        }
    }
    a.b();

위 코드의 실행 결과

이렇게 보면 매우 알기 쉽다


간단하게 외우려면 호출하는 함수 앞에 .(점)이 있으면 그 객체가 this, 없으면 window객체가 this라고 외우면 된다

'2.함수내부'에서 살펴본 코드의 함수 fw();도 window.fw();가 생략된 모양이라고 생각하자

내부함수의 this 바인딩

    var tValue = 1000; ----⑥

    var tObj = {
        tValue : 1, ----①
        f1 : function () {
            this.tValue += 1; ----②
            console.log(this.tValue); ----③

            f2 = function () {
                this.tValue += 1; ----④
                console.log(this.tValue); ----⑤
            }
            f2();
        }
    }
    tObj.f1();

이 코드는 결과 값이 어떻게 나올까?

배운대로 생각해보자

  1. tObj.f1()이 실행되었다
  2. ②의 this는 tObj이므로 ①을 1증가시키고 ③출력한다 (출력값: 2)
  3. f2()가 실행되었다
  4. f1함수 안에서 증가시킨 ②(값: 2)를 공유하여 ④에서 한번 더 1을 증가시키고 ⑤출력한다 (출력값: 3)

출력값 2와 3이 나오면 행복할 것 같다

우리를 행복하게 하지 않는 실행 결과

우리는 불행해졌다..

분명 배운대로 푼 것 같은데 어디서 잘못된 걸까?

간단하게 외우려면 호출하는 함수 앞에 .(점)이 있으면 그 객체가 this, 없으면 window객체가 this라고 외우면 된다

...!

3. f2()가 실행되었다

그렇다 우리는 코드의 중괄호만 보고 ④의 this가 tObj라고 착각했다..

사실 ④의 this는 window 객체를 가르키고 있어서 ⑥의 tValue값을 1증가시키고 출력한 것이다

이해는 했는데.. ④같은 내부 함수에서도 ①과 같은 값을 공유하고 싶을 땐 어떡해야할까?

내부 함수가 접근 가능한 상위 함수(상위 컨텍스트)에 this를 담아서 변수에 저장하고

그 변수에 접근시키면 내부 함수들도 모두 같은 객체에 접근할 수 있게된다

var tValue = 1000;

    var tObj = {
        tValue : 1,
        f1 : function () {
            var that = this; ----①

            this.tValue += 1;
            console.log(this.tValue);

            f2 = function () {
                that.tValue += 1; ----②
                console.log(that.tValue);
            }
            f2();
        }
    }
    tObj.f1();

위 코드의 실행 결과

①처럼 f1의 this객체를 that에다 저장하고 ②에서 that으로 접근하면 내부 함수인 f2에서도 f1의 this객체에 접근이 가능하다!

4. 생성자

    function Person(name, age) {
        this.name = name;
        this.age = age;
    }

    a = new Person('aaa',10);

생성자에서의 this는 자바와 마찬가지로 인스턴스를 가르키는데

(생성자 함수 포스팅)에서 더 자세히 다뤘다


5. call(), apply(), bind()

위에서 상황에 따라서 this가 가르키는 객체가 어떻게 달라지는지 살펴봤다

자바스크립트에서는 this가 묵시적으로 binding이 되지만 개발자가 원하는 객체에 명시적으로 binding시키는 방법들도 존재한다

바로 call(), apply(), bind()메소드들을 사용해서 명시적 binding이 가능한데

이 메소드들은 묵시적으로 모든 함수들에 내장되어있다

    var who = 'im window';

    var tObj = {
        who : 'im tObj'
    };

    var cab = function (a,b,c) {
        console.log(this.who, a, b, c);
    }

    cab.apply(tObj,[1,2,3]);
    cab.call(tObj, 1, 2, 3);
    (cab.bind(tObj, 1, 2))(3);

코드 아래 부분의 메소드들은 모두 실행결과가 동일하다

위 코드의 결과

우리가 배운 것처럼 cab함수 안에서 사용된 this.who는 전역 객체에 binding 돼야 하기 때문에 window의 변수인 'im window'가 나와야하지만 tObj객체의 프로퍼티인 who가 출력되고 있다

저 3가지의 코드들은 모두 cab함수를 실행시키는 코드들인데 다른 점이 있다면 앞에 인자로서 객체를 하나 더 받고

this를 인자로 받아온 객체에 binding하는 코드들이다

그렇기 때문에 전역 객체에 초기화 된 'im window'가 아닌 인자로 받아온 tObj에 binding되어 'im tObj'가 출력되는 것

그렇다면 이제 3가지 메소드들의 차이점을 알아보자

apply()와 call()

이 둘의 차이점은 매우 간단하다

앞에 객체를 인자로 받는 것 까진 동일하고 그 뒤에 받는 인자들의 형식만 다르다

function.apply(thisArg, argArray);
fucntion.call(thisArg, arg1,arg2,...);

보는 바와 같이 apply()는 뒤에 받는 인자들은 배열의 형식이고

call()은 인자 하나하나 낱개로 받는다

bind()

bind()는 위의 두 메소드와 살짝 다르다

두 메소드는 함수를 호출하지만 bind()는 넘겨받은 인자들로 새로운 함수를 생성한다

위의 코드를 다시 살펴보면 살짝 의아한 점이 있다

bind()는 호출이아니라 생성이라고 했는데 어째서 실행결과는 3가지가 나왔을까?

바로 bind()메소드를 즉시 실행 함수 형태로 불렀기 때문인데 사용법은 간단하다

함수를 ()로 한번 감싹고 메소드 호출하듯 (arg1, arg2,...);를 뒤에 붙여주면 된다

`(cab.bind(tObj, 1, 2))(3);`
와 같이 말이다

위의 즉시 실행 함수를 풀어서 쓰면 이렇게 된다

    var tF = cab.bind(tObj, 1, 2);

    tF(3);

 

cab함수의 this를 tObj로 binding하고 기존에 설정되어있던 인자 a,b,c의 a,b는 1,2로 고정되는 함수를 tF로 만들었다

결과적으로 tObj 객체를 this로 가르키고 있고

1,2를 기본적으로 출력하고 +@로 하나의 인자를 받아서 출력하는 tF함수를 만든 것이다

 

'JavaScript > 기초' 카테고리의 다른 글

호출 스택, 이벤트 루프  (0) 2020.10.02
생성자 함수  (0) 2020.10.01
실행 컨텍스트와 스코프  (0) 2020.09.28
호이스팅  (0) 2020.09.28
자바스크립터의 데이터 타입  (0) 2020.09.27