만일 생성자함수를 통해 객체를 생성하려고 할 때 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 메서드를 가지는 것은 아니다.
함수는 객체이다. 하지만 일반적인 객체와 다르게 호출을 할 수 있다. 그래서 함수는 몇 가지 메서드를 더 가지게 되는데 그중에 하나가 [[call]] 이라는 내부 메서드이다. 모든 함수는 이 내부 메서드를 가지고 있다.
그렇기 때문에 생성자 함수도 new 키워드 없이 호출될 수 있다는 것이다.
하지만 모든 함수가 constructor는 아니다. 내부 메서드 [[construct]]를 가진 함수만이 constructor가 될 수 있다.
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 연산자를 통해 함수를 호출하면 내부 메서드 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 키워드 없이 호출이 된다면 적절한 에러를 띄워줄 수 있을 것이다.
function Circle(radius){
if(!new.target){
return new Circle(radius);
}
this.radius = radius;
}
new.target
은 생성자함수가 new 키워드로 호출되었을 경우 생성자함수 자신을 가리키고 그렇지 않으면 undefined가 된다.
new.target
이 지원되지 않는 브라우저가 있다.
new 키워드를 통해 생성자함수를 생성하면 생성자함수가 생성할 인스턴스 this는 생성자함수와 프로토타입에 의해 연결된다.
따라서 다음과 같이 에러체크를 할 수도 있다.
function Circle(radius){
if(!(this instanceof Circle)){
return new Circle(radius);
}
this.radius = radius;
}
프로토타입에 의해 연결되는 순간은 생성자함수가 빈객체를 만들고 this에 바인딩하는 순간이다. 따라서 instanceof 검사를 하는 시점에 만일 new 키워드를 통해 호출되지 않았다면 this는 전역객체를 가리키고 있다.