ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 객체의 메서드를 호출할 때 this 바인딩
    개발 2020. 4. 16. 21:37

    객체의 프로퍼티가 함수일 경우, 이 함수를 메서드라고 부릅니다.

    이러한 메서드를 호출할 때, 메서드 내부에서 사용된 this는 해당 메서드를 호출한 객체로 바인딩 됩니다.

     

    다음 예제를 통해서 객체의 메서드 호출 사용시의 this 바인딩 과정을 보겠습니다.

    // obj 객체 생성
    let obj1 = {
      name: 'foo',
      sayName: function(){
        console.log(this.name);
      }
    };
    
    // obj2 객체 생성
    let obj2 = {
      name: 'bar'
    };
    
    // obj2 객체에 sayName() 메서드를 만들고 이 메서드에 obj.sayName()을 할당하여 참조하기
    obj2.sayName = obj1.sayName;
    
    obj1.sayName();  // foo
    obj2.sayName();  // bar

    obj1 객체와 obj2 객체는 name 프로퍼티와 sayName() 메서드가 있습니다.

    sayName() 메서드는 this.name 값을 콘솔로 출력하는 간단한 함수이고, obj1와 obj2 객체로부터 각각 호출됩니다.

    이때 sayName() 메서드에서 사용된 this는 자신을 호출한 객체에 바인딩 됩니다.

     

    obj2 객체에 sayName이라는 프로퍼티의 값으로 obj1 에서 선언한 sayName()함수를 참조하였고 obj1.sayName() 과 obj2.sayName() 은 같은 주소 값을 바라보고 있습니다. 같은 주소 값을 바라보고 있는다는 말은 같은 함수를 참조하고 있다는 말과 같기 때문에 참조관계라고 말할 수 있는 것입니다.

     

    하지만 obj1.sayName과 obj2.sayName은 참조관계 즉, 같은 주소 값을 바라보게 되었다고 해서 obj2.sayName() 메서드 안의 this가 obj1이 되는 것은 아닙니다. 앞서 설명했듯이 메서드 내부에서 사용된 this는 해당 메서드를 호출할 때 객체에 바인딩이 된다고 했습니다. 그렇기 때문에 sayName() 메서드를 호출한 객체를 가리키게 되어 obj1.sayName()은 foo를 출력하고 obj2.sayName()은 bar를 출력하는 것입니다.

     

     

    이 과정을 좀 더 이해하기 쉽게 그림으로 설명하자면 아래와 같습니다.

     

     

    더 다양한 예제로 객체의 메서드를 호출할 때 this를 알아보겠습니다.

    다음은 함수를 객체 외부에서 선언하고, 객체 안에서 호출하는 경우에도 this는 해당 객체의 this를 참조하게 되는 예제 입니다.

     

    let person = {
      name: 'hyojin',
      hello: function() {
        // 여기서의 this는 앞서 설명한대로 객체 자체를 가리킵니다. 즉, this.name은 'hyojin'이 됩니다.
        console.log('hello ' + this.name);
      }
    }
    
    // 일반 함수 선언
    function intro() {
      // 여기서 this는 전역 객체 Window이므로, window에는 name이라는 프로퍼티가 없기 때문에 
      // 만약 실행시키면 아래와 같이 name 값은 undefined가 뜹니다.
      console.log('My name is ' + this.name);
    }
    
    intro();  // undefined
    
    // person 객체의 intro 속성에 일반 함수 intro 메서드 추가
    person.intro = intro;
    
    // intro함수가 person 객체에서 호출되었으므로 My name is hyojin이 출력됩니다.
    person.intro();

    위 코드와 같이 객체의 메서드가 될 intro 함수를 객체의 외부에 선언한 후에 person.intro = intro; 라고 메서드를 추가해주었습니다.

    추가한 후에 person.intro(); 를 호출하면 person객체에서 호출되었으므로 this.name 은 hyojin 으로 잘 나옵니다.

    하지만 그냥 intro(); 를 호출했을 때에는 호출된 시점이 window영역이므로 window에는 name 프로퍼티가 없기 때문에 undefined가 출력됩니다. 

     

    그렇다면 이와는 반대로 객체 외부에서 이 메서드를 변수에 담고, 참조관계가 된 이 변수를 window영역에서 호출하면 this가 어떻게 되는지 알아보겠습니다.

    // person 객체의 welcome 속성에 함수를 선언합니다.
    person.welcome = function () {
        // person.welcome()을 호출하면 welcome hyojin이 출력될 것입니다.
        console.log('welcome ' + this.name);
    }
    
    // 위 코드의 welcome 함수를 객체 외부에서 welcome이라는 변수에 할당하여 참조
    let welcome = person.welcome;
    
    // 객체 외부이므로 this는 Window 객체가 되어서 welcome + undefined가 출력됩니다.
    welcome();

    person.welcome으로 객체의 속성에 함수를 바로 선언해주었고 그 함수를 객체 외부에서 변수에 담아서 참조관계를 만들었습니다.

    그리고서 이 변수에 담긴 함수를 welcome(); 으로 호출한 결과 person객체에서 호출된 것이 아닌 객체 외부인 window영역에서 그냥 호출했기 때문에 this가 window가 되었고 출력 결과로 'welcome '은 잘 찍혔으나 그 다음인 this.name은 undefined 가 출력되는 것을 볼 수있습니다.

     

    그럼 또 다른 객체를 만들고 그 객체 내부에서 이 welcome 함수를 호출하면 어떻게 될까요?

    // 다른 객체 갱성
    let person2 = {
        name: 'jack',
        // person.welcome 함수를 person2.welcome 속성에 참조
        welcome: person.welcome
    }
    
    person2.welcome();  // 'welcome jack'

    person2 라는 새로운 객체를 만들고 이 객체 안에 있는 welcome 이라는 속성 값으로 person.welcome 메서드를 참조하였습니다.

    이 welcome 함수를 호출한 객체가 person객체가 아닌 person2 객체이기 때문에 this가 person2를 가리키게 되어 person2.welcome();을 호출한 결과로 'welcome jack' 이 잘 출력되는 것을 볼 수있습니다.

     

    여기서 제가 많이 헷갈리기도 했고 아마 저처럼 개발이 처음이신 분들에게는 많이 헷갈리는 문제일 것이라고 생각되는 부분이 하나 있습니다.

    객체의 메서드에서 내부함수를 선언하는 경우는 this가 무엇을 가리키게 될까요? 다음 예시를 통해 알아보겠습니다.

    let user = {
        name: 'steve',
        hello: function() {
            // 객체의 메서드 안에서 함수를 선언하는 것이므로 이 함수는 내부 함수 입니다.
            function getName() {
                // 여기서 this는 무엇을 가리키고 있을까요?
                return this.name;
            }
            console.log(getName()); // 내부 함수를 출력
        }
    }
    user.hello(); // undefined

    user라는 객체가 있고 이 객체에는 name프로퍼티와 hello라는 메서드가 정의되어있습니다.

    메서드 hello()의 할 일은 내부 함수인 getName();을  호출하는 것이고 내부 함수인 getName() 의 할 일은 this.name 값을 return해주고 있습니다.

     

    결과를 보면 객체의 외부에서 user.hello();를 호출했을 때, this.name이 user 객체의 name 프로퍼티를 가리켜 steve가 출력 될 것을 예상했지만 undefined가 뜹니다.

    그 이유는 객체의 메서드에서 this는 객체를 가리키고 있던 것과는 다르게,  내부 함수인 getName()의 this는 window 객체를 가리키고 있습니다. 내부함수는 엄밀히 말해 메서드가 아니기 때문에 단순 함수 호출 규칙에 따라 window를 가리키고 있기 때문입니다.

     

    위의 코드를 우리가 의도한 결과대로 나올 수 있게 하려면 각각 다른 문맥의 this를 필요에 따라 변경할 수 있도록 자바스크립트에서 제공해주는 call(), apply(), bind() 등의 함수가 있는데, 여기서는 call()을 사용하여 의도한 결과 값이 잘 출력되는지 보겠습니다.

    let user = {
        name: 'steve',
        hello: function() {
            // 객체의 메서드 안에서 함수를 선언하는 것이므로 이 함수는 내부 함수 입니다.
            function getName() {
                // 여기서 this는 무엇을 가리키고 있을까요?
                return this.name;
            }
            // call()을 통해 현재 문맥에서의 this(user 객체)를 바인딩해주었습니다.
            console.log(getName.call(this));
        }
    }
    user.hello(); // 'steve'

     

    보시는 것처럼 this가 잘 바인딩 되어 user.hello(); 를 호출해도 steve를 출력하는 것을 볼 수 있습니다.

     

    내부함수를 호출했을 때 this에 관하여 짧게 예시를 통하여 설명해 보았는데 이 예시 하나로 이 맥락들을 이해하기에는 조금 힘든 부분이 있습니다. 

    이 부분에 대해서는 함수를 호출할 때의 this 바인딩 에서 좀 더 깊게 다루고 있으니 참고해주시면 감사하겠습니다.

     

    '개발' 카테고리의 다른 글

    생성자 함수를 호출할 때 this 바인딩  (0) 2020.04.20
    함수를 호출할 때의 this 바인딩  (0) 2020.04.19
    함수의 인수 (rest와 arguments 객체)  (0) 2020.04.14
    prototype과 prototype Chain  (0) 2020.04.14
    생성자와 new  (0) 2020.04.12
Designed by Tistory.