ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 생성자 함수를 호출할 때 this 바인딩
    개발 2020. 4. 20. 22:46

    자바스크립트에서 객체를 생성하는 방법에는 생성자 함수를 이용하는 방법이 있습니다.

    생성자 함수는 말 그대로 자바스크립트의 객체를 생성하는 역할을 하는데 기존 함수에 new 연산자를 붙여서 호출하면 해당 함수는 생성자 함수로 동작합니다.

     

    먼저 생성자 함수가 동작하는 방식에 대해서 알아보겠습니다.

     

    1. 빈 객체 생성 및 this 바인딩

     

    생성자 함수 코드가 실행되기 전 빈 객체가 생성됩니다. 바로 이 객체가 생성자 함수가 새로 생성하는 객체이고 이 객체는 this로 바인딩 됩니다. 즉, 이후 생성자 함수의 코드 내부에서 사용된 this는 이 빈 객체를 가리킵니다.
    하지만 엄밀히 말하면 여기서 생성된 객체가 빈 객체는 아닙니다. 자바스크립트의 모든 객체는 자신의 부모인 프로토타입 객체와 연결되어 있으며, 이를 통해 부모 객체의 프로퍼티나 메서드를 상속받아 사용할 수 있기 때문입니다.

    이렇게 생성자 함수가 생성한 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가리키는 객체를 자신의 프로토타입 객체로 설정합니다.


    (프로토 타입에 관련된 자세한 내용은 prototype과 prototype Chain 컨텐츠에서 다루고 있으니 참고해주시면 감사하겠습니다.)

     

    2. this를 통한 프로퍼티 생성

     

    함수 코드 내부에서 this를 사용하여 1번에서 생성된 객체에 동적으로 프로퍼티나 메서드를 생성할 수 있습니다.

     

    3. 생성된 객체 리턴

     

    리턴문이 동작하는 방식은 경우에 따라 다르지만 가장 일반적인 경우로 특별하게 리턴문이 없을 경우, this로 바인딩된 새로 생성한 객체가 리턴됩니다. 

     

    이 처럼 간단하게 생성자 함수가 동작하는 방식에 대해 알아보았으니 예제를 통해 더 자세히 알아보도록 하겠습니다.

    function Person(name){
      // 함수 코드 (여기에서는 this.name = name;) 실행 전
      this.name = name;
      // 함수 리턴
    }
    
    // foo 객체 생성
    let foo = new Person('foo');
    console.log(foo.name);  // foo

    Person이라는 생성자 함수를 선언하고 이 함수를 통해 foo라는 객체를 만드는 예제입니다.

    new Person()과 같이 Person() 함수를 new로 호출하면, Person()은 생성자 함수로 동작합니다.

     

    이 코드의 과정을 순차적으로 설명해보자면 다음과 같습니다.

     

    1. Person() 함수가 생성자로 호출되면, 함수 코드가 실행되기 전에 빈 객체가 생성됩니다. 여기서 생성된 빈 객체는 Person() 생성자 함수의 prototype 프로퍼티가 가리키는 객체(Person.prototype 객체)를 __proto__ 링크로 연결해서 자신의 프로토타입으로 설정합니다. 그리고 이렇게 생성된 객체는 생성자 함수 코드에서 사용되는 this로 바인딩 됩니다.
    2. this가 가리키는 빈 객체에 'foo'를 인자로 넘겨 name 이라는 프로퍼티를 생성했습니다.
    3. 리턴값이 특별이 없으므로 this로 바인딩한 객체가 생성자 함수의 리턴값으로 반환되어 foo 변수에 저장됩니다.

    이 과정을 좀 더 쉽게 이해하기 위해 그림으로 나타내면 다음과 같습니다.

     

     

     

     

     

    생성자 함수에 new를 붙이지 않고 호출할 경우

     

    일반 함수와 생성자 함수의 구별을 위해서 생성자 함수의 맨 앞글자는 대문자로 써주는 관례가 있는데, 이 것을 제외하고 보면 일반 함수와 생성자 함수는 별도의 차이가 없습니다. new를 붙여서 함수를 호출하면 생성자 함수로 동작하는 것입니다.

    때문에 객체 생성을 목적으로 작성한 생성자 함수를 new 없이 호출하거나 일반 함수에 new를 붙여서 호출할 경우 코드에서 오류가 발생할 수 있습니다. 그 이유는 일반 함수 호출과 생성자 함수를 호출할 때 this 바인딩 방식이 다르기 때문입니다.

     

    일반 함수 호출의 경우 this가 window 전역 객체에 바인딩되는 반면에, 생성자 함수 호출의 경우 this는 새로 생성되는 빈 객체에 바인딩 되기 때문입니다. 

    function Person(name){
      // 함수 코드 (여기에서는 this.name = name;) 실행 전
      this.name = name;
      // 함수 리턴
    }
    
    // new를 사용하지 않고 생성자 함수 호출
    let foo = Person('foo');
    
    console.log(foo);  // undefined
    console.log(foo.name);  // Uncaught TypeError: Cannot read property 'name' of undefined
    console.log(window.name);  // 'foo'

    생성자 함수 Person()을 new 없이 일반 함수 형태로 호출할 경우, this는 함수 호출이므로 전역객체인 window 객체로 바인딩 됩니다.

    따라서 이 코드는 Person 객체를 생성해서 이를 foo 변수에 저장하려 했던 의도와는 다르게 this가 바인딩 된 window 객체에 동적으로 name프로퍼티가 생성이 되어 window.name으로 접근해야만 foo를 출력해 내는 것입니다.

     

    Person() 함수는 리턴값이 특별히 없습니다. 생성자 함수는 별도의 리턴값이 정해져 있지 않은 경우에 새로 생성된 객체가 리턴되지만, 일반 함수를 호출할 때는 undefined가 리턴됩니다. 따라서 foo는 undefined 값이 출력 된 것입니다.

     

Designed by Tistory.