[자바스크립트]

[JavaScript/DeepDive]17.생성자 함수에 의한 객체 생성

ki7348 2021. 5. 18. 20:35
  • [17.1] Object 생성자 함수
  • new 연산자와 함께 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환한다.
    • 빈 객체를 생성한 이후 프로퍼티 또는 메서드를 추가하여 객체를 완성할 수 있다.
      • // 빈 객체의 생성
        const person = new Object( );

        // 프로퍼티 추가
        person.name = 'Lee';
        person.sayHello = function( ){
             console.log('Hi! My name is ' + this.name);
        };

        console.log(person); // {name: "Lee", sayHello: f}
        person.sayHello( ); // Hi! My name is Lee
  • 생성자 함수에 의해 생성된 객체를 인스턴스라 한다.

 

 

 

  • [17.2] 생성자 함수
  • 객체 리터럴에 의한 객체 생성 방식은 단 하나의 객체만 생성한다.
    • 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야 하기 때문에 비효율적이다.
  • 생성자 함수에 의한 객체 생성 방식은 마치 객체(인스턴스)를 생성하기 위한 템플릿(클래스)처럼 생성자 함수를 사용하여 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있다.
    • // 생성자 함수
      function Circle(radius) {
           // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
           this.radius = radius;
           this.getDiameter = function ( ){
                return 2 * this.radius;
           };
      }

      // 인스턴스의 생성
      const circle1 = new Circle(5); // 반지름이 5인 Circle 객체를 생성
      const circle2 = new Circle(10); // 반지름이 10인 Circle 객체를 생성
      console.log(circle1.getDiameter( )); // 10
      console.log(circle2.getDiameter( )); // 20
  • 생성자 함수는 일반 함수와 동일한 방법으로 생성자 함수를 정의하고 new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다.
    • 만약 new 연산자와 함께 생성자 함수를 호출하지 않으면 생성자 함수가 아니라 일반 함수로 동작한다.
      • const circle3 = Circle(15); // 일반 함수로서 호출된다.
  • 생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿(클래스)으로서 동작하여 인스턴스를 생성하는 것 생성된 인스턴스를 초기화(인스턴스 프로퍼티 추가 및 초기값 할당)하는 것이다.
    • 생성자 함수가 인스턴스를 생성하는 것은 필수이고, 생성된 인스턴스를 초기화하는 것은 옵션이다.
      • new 연산자와 함께 생성자 함수를 호출하면 자바스크립트 엔진은 다음과 같은 과정을 거쳐 암묵적으로 인스턴스를 생성하고 인스턴스를 초기화한 후 암묵적으로 인스턴스를 반환한다.
  1. 인스턴스 생성과 this 바인딩
  2. 인스턴스 초기화
  3. 인스턴스 반환
  • function Circle(radius) {
         // 1. 암묵적으로 빈 객체가 생성되고 this에 바인딩된다.

         // 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
         this.radius = radius;
         this.getDiameter = function ( ) {
              return 2 * this.radius;
         };
         
         // 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
    }

    // 인스턴스 생성. Circle 생성자 함수는 암묵적으로 this를 반환한다.
    const circle = new Circle(1);
    console.log(circle); // Circle { radius: 1, getDiameter: f}
    • 만약 this가 아닌 다른 객체를 명시적으로 반환하면 this가 반환되지 못하고 return 문에 명시한 객체가 반환된다.
  • 내부 메서드 [[Call]]과 [[Construct]]
  • 함수는 객체 이므로 일반 객체와 동일하게 동작할 수 있다.
  • 하지만 일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.
    • 함수 객체는 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드는 물론, 함수로서 동작하기 위해 함수 객체만을 위한 [[Environment]], [[FormalParameters]] 등의 내부 슬롯과 [[Call]], [[Construct]] 같은 내부 메서드를 추가로 가지고 있다.
  • 함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 [[Call]]이 호출되고 new 연산자와 함께 생성자 함수로서 호출되면 내부 메서드 [[Construct]]가 호출된다.
  • 내부 메서드 [[Call]]을 갖는 함수 객체를 callable이라 하며, 내부 메서드 [[Construct]]를 갖는 함수 객체를 constructor, [[Construct]]를 갖지 않는 함수 객체를 non-constructor라고 부른다.
    • callable은 호출할 수 있는 객체, 즉 함수를 말하며, constructor는 생성자 함수로서 호출할 수 있는 함수, non-constructor는 객체를 생성자 함수로서 호출할 수 없는 함수를 의미한다.
  • 함수 객체는 callable이면서 constructor이거나 callable이면서 non-constructor이다.
    • 즉, 모든 객체는 호출할 수 있지만 모든 함수 객체를 생성자 함수로서 호출할 수 있는 것은 아니다.
  • constructor와 non-constructor의 구분
    • constructor: 함수 선언문, 함수 표현식, 클래스(클래스도 함수다)
    • non-constructor: 메서드(ES6 메서드 축약 표현), 화살표 함수
      • // 일반 함수 정의: 함수 선언문, 함수 표현식
        function foo( ) { }
        const bar = function ( ) { };

        // 프로퍼티 x의 값으로 할당된 것은 일반 함수로 정의된 함수다. 이는 메서드로 인정하지 않는다.
        const baz = {
             x: function ( ) { }
        };

        // 일반 함수로 정의된 함수만이 constructor다.
        new foo( ); // -> foo { }
        new bar( ); // -> bar { }
        new baz.x( ); // -> x { }

        // 화살표 함수 정의
        const arrow = ( ) => { };
        new arrow( ); // TypeError: arrow is not a constructor

        // 메서드 정의: ES6의 메서드 축약 표현만 메서드로 인정한다.
        const obj = {
             x( ) { }
        };

        new obj.x( ); // TypeError: obj.x is not a constructor
  • new 연산자
  • new 연산자와 함께 호출하는 함수는 non-constructor가 아닌 constructor이어야 한다.
  • 반대로 new 연산자 없이 생성자 함수를 호출하면 일반 함수로 호출된다.
    • 함수 객체의 내부 메서드 [[Construct]]가 호출되는 것이 아니라 [[Call]]이 호출된다.
      • const Circle = Circle(5);
        // new 연산자 없이 생성자 함수 호출하면 일반 함수로 호출된다.
  • new.target
  • 생성자 함수가 new 연산자 없이 호출되는 것을 방지하기 위해 파스칼 케이스 컨벤셔을 사용한다 하더라도 실수는 언제나 발생할 수 있다.
    • 이러한 위험성을 회피하기 위해 ES6에서는 new.target을 지원한다.
  • new.target은 this와 유사하게 constructor인 모든 함수 내부에서 암묵적인 지역 변수와 같이 사요되며 메타 프로퍼티라 부른다.
  • 함수 내부에서 new.target을 사용하면 new 연산자와 함께 생성자 함수로서 호출되었는지 확인할 수 있다.
  • new 연산자와 함께 생성자 함수로서 호출되면 함수 내부의 new.target은 함수 자신을 가리킨다.
  • new 연산자없이 일반 함수로서 호출된 함수 내부의 new.target은 undefined이다.
    • // 생성자 함수
      function Circle(radius) {
           // 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target은 undefined다.
           if(!new.target) { 
                // new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다.
                return new Circle(radius);
           }
           this.radius = radius;
           this.getDiameter = function ( ) {
                return 2 * this.radius;
           };
      }

      // new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로서 호출된다.
      const circle = Circle(5);
      console.log(circle.getDiameter( ));