생성자함수에 의한 객체생성

박찬욱·2023년 9월 12일
0

Basic JavaScript

목록 보기
10/13

만일 생성자함수를 통해 객체를 생성하려고 할 때 new키워드없이 생성자함수를 호출할 경우에는 어떻게 될까? 이것이 문제가 된다면 어떻게 오류를 잡아줄 수 있을까?

생성자 함수

생성자 함수가 등장한 배경은 객체 리터럴에 의한 객체 생성 방식의 문제점 때문이다. 객체 리터럴은 객체를 생성할 때 간편하게 생성할 수 있지만 같은 기능을 수행하는 동일한 객체를 여러번 반복해서 작성해야한다는 번거로움이 있다.

만일 붕어빵 기계처럼 틀이 있고 이 틀로 붕어빵을 계속 만들어낼 수 있는 것처럼 객체도 이렇게 생성하면 좋지않을까?

생성자함수를 사용하면 프로퍼티 구조가 동일한 객체 여러개를 간편하게 생성할 수 있다.

function Circle(radius) {
  console.log(this);

  this.radius = radius;
  this.getDiameter = () => {
    return 2 * this.radius;
  };
}

const circle1 = new Circle(5);
const circle2 = new Circle(10);

여기서 this는 함수가 호출되는 방식에 따라 동적으로 바인딩이 결정되는데 생성자 함수로써 new 키워드를 통해 호출되는 경우에는 생성자 함수가 생성할 인스턴스에 바인딩이 된다.

간단하게 new 키워드를 통해 인스턴스를 생성할 때 과정을 살펴보면 다음과 같다.

  • 런타임이전에 생성자함수가 빈 객체를 만들고 이것을 this에 바인딩한다.
  • 생성자함수 내부의 코드를 읽으면서 this에 바인딩된 인스턴스에 프로퍼티나 메서드를 추가 및 초기화한다.
  • 인스턴스가 바인딩된 this를 암묵적으로 반환한다.

constructor / non-constructor

모든 함수는 호출이 가능하지만 모든 함수가 constructor 메서드를 가지는 것은 아니다.

함수는 객체이다. 하지만 일반적인 객체와 다르게 호출을 할 수 있다. 그래서 함수는 몇 가지 메서드를 더 가지게 되는데 그중에 하나가 [[call]] 이라는 내부 메서드이다. 모든 함수는 이 내부 메서드를 가지고 있다.

그렇기 때문에 생성자 함수도 new 키워드 없이 호출될 수 있다는 것이다.

하지만 모든 함수가 constructor는 아니다. 내부 메서드 [[construct]]를 가진 함수만이 constructor가 될 수 있다.

  • constructor : 함수선언문, 함수표현식, 클래스
  • non-constructor : ES6축약 메서드, 화살표함수
function foo() {}
const bar = function () {};

const baz = {
  x: function () {},
};

new foo();
new bar();
new baz.x();

const arrow = () => {};
new arrow();

const obj = {
  x() {},
};
new obj.x();

new 연산자

new 연산자를 통해 함수를 호출하면 내부 메서드 call이 아닌 construct가 호출된다. 단, 이때 호출되는 함수는 메서드나 화살표함수여서는 안된다.

function add(x,y) {
  return x+y;
}

let inst = new add(); //inst는 빈 객체

function Circle(radius){
  this.radius = radius;
  this.getDiameter = function () {
    return 2 * this.radius;
  };
}

const circle = Circle(5); // 리턴값이 없으므로 undefined
console.log(radius); // 5
console.log(getDiameter()); // 10

Circle은 생성자함수로 호출된 것이 아니라 일반함수로 호출되었다. 일반함수로 호출될 경우 this는 전역객체에 바인딩이되기때문에 radius와 getDiameter함수는 전역객체의 프로퍼티와 메서드가 된다.

에러표시

그렇다면 맨 위의 질문에 답이 나왔다.
생성자 함수는 일반 함수로 호출이 가능하지만 원하는 객체를 생성해낼 순 없다. 하지만 new 키워드 없이 호출될 수 있기때문에 만일 new 키워드 없이 호출이 된다면 적절한 에러를 띄워줄 수 있을 것이다.

new.target

function Circle(radius){
  if(!new.target){
    return new Circle(radius);
  }
  this.radius = radius;
}

new.target은 생성자함수가 new 키워드로 호출되었을 경우 생성자함수 자신을 가리키고 그렇지 않으면 undefined가 된다.

instanceof

new.target이 지원되지 않는 브라우저가 있다.
new 키워드를 통해 생성자함수를 생성하면 생성자함수가 생성할 인스턴스 this는 생성자함수와 프로토타입에 의해 연결된다.

따라서 다음과 같이 에러체크를 할 수도 있다.

function Circle(radius){
  if(!(this instanceof Circle)){
    return new Circle(radius);
  }
  this.radius = radius;
}

프로토타입에 의해 연결되는 순간은 생성자함수가 빈객체를 만들고 this에 바인딩하는 순간이다. 따라서 instanceof 검사를 하는 시점에 만일 new 키워드를 통해 호출되지 않았다면 this는 전역객체를 가리키고 있다.

profile
대체불가능한 사람이다

0개의 댓글