모던자바스크립트 - 생성자 함수에 의한 객체 생성
Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환한다. 이후 프로퍼티 또는 메서드를 추가하여 객체를 완성할 수 있다.
const person = new Object();
console.log(person) // {}
person.name = '반원재'
console.log(person) // {name: '반원재'};
객체를 생성하는 방법은 객체 리터럴을 사용하는 것이 더 간편하기 때문에 Object 생성자 함수를 사용해 객체를 생성하는 방식은 그다지 유용해 보이지 않는다.
하지만 생성자 함수를 사용하면 프로퍼티 구조가 동일한 객체 여러개를 간편하게 생성할 수 있다.
// 생성자 함수
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
//생성자 함수를 쓰면 리터럴 방식과는 다르게 간편하게 유사한 객체 여러개 생성 가능.
const circle1 = new Circle(5);
const circle2 = new Circle(10);
생성자 함수는 이런식으로 일반 함수와 동일한 방법으로 정의하고 new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다.
- 런타임 이전에 암묵적으로 빈 객체가 생성되고 생성된 빈객체가 this에 바인딩 된다.
- 이후 개발자가 정의한 인스턴스 초기화 로직들이 실행된다.
- 마지막으로 생성자 함수 내부의 모든 처리가 끝나면 인스턴스가 바인딩 된 this가 암묵적으로 반환된다. 이게 new Circle()만 해도 객체의 참조값을 변수에 할당이 가능한 이유다.
만약 this가 아닌 다른 객체를 반환하면 명시한 객체가 반환된다.
// 생성자 함수
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
// 이렇게 다른 객체를 리턴하면
return {};
}
const circle1 = new Circle(5); // {}
const circle2 = new Circle(10); // {}
반면에 원시값을 반환하면 원시 값 반환은 무시하고 암묵적으로 다시 this를 반환한다.
// 생성자 함수
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
// 이렇게 원시값을 반환하면 무시하고 this를 반환한다.
return 100;
}
const circle1 = new Circle(5); // 객체 잘 생성되고 할당된다.
const circle2 = new Circle(10);
따라서 생성자 함수 내부에서 return문은 반드시 생략하자.
함수는 객체라서 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드는 물론 함수 객체만의 내부 슬롯과 [[call]], [[construct]] 같은 내부 메서드를 추가로 가지고 있다.
call 내부 메서드를 통해 우리는 함수 객체를 호출할 수 있고, contruct 메서드를 통해서 우리는 생성자 함수로서 호출이 가능하다.
함수객체는 callable이면서 contructor이거나 callable이면서 non-constructor이다. 즉, 모든 함수 객체는 호출할 수 있지만 모든 함수객체를 생성자 함수로서 호출할 수 있는 것은 아니다.
constructor와 non-constructor의 구분은 아래와 같다.
// 일반함수 선언문
function foo() {}
//함수 표현식
const bar = function () {}
// 일반 메서드 표현
const xyz = {
x: function () {};
}
new foo(); //ok
new bar(); //ok
new xyz.x(); //ok
// 화살표 함수
const arrow = () => {}
// ES6의 메서드 축약 표현
const obj = {
x() {}
}
new arrow(); //에러
new obj.x(); //에러
new 연산자 없이 생성자 함수를 호출하면 생성자가 아닌 일반함수로 호출된다. 생성자 함수를 호출하고 싶다면 반드시 new를 붙여주자.
이처럼 생성자 함수와 일반함수간의 차이가 거의 없기 때문에 보통 구분을 위해서 생성자 함수명은 파스칼로 명명한다.
생성자 함수가 new없이 호출되는 것을 방지하기 위해서 ES6에서는 new.target을 활용할 수 있다.
// 생성자 함수
funcion Circle(radius) {
// 이 함수가 new와 함께 호출되지 않으면 undefined
if(!new.target) {
return new Circle(radius);
}
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
}
}
// 이제 new 연산자 없어도 생성자 함수로 호출된다.
const circle = Circle(5)
대부분의 빌트인 생성자 함수(Object, Function 등등)은 new 연산자와 함께 호출되었는지를 확인한 후 적절한 값을 반환한다. 따라서 new 없이 호출해도 생성자 함수로 동작한다.
다만 String, Number, Boolean 생성자 함수는 new 없이 사용하면 형변환 효과만 있고 생성자 효과는 없다.