Object 생성자 함수
new 연산자와 함께 Object 생성자 함수를 호출하면 객체를 생성할 수 있다. 생성자 함수란 new 연산자를 통해 호출하여 객체를 생성하는 함수를 뜻하며 이 때 반환되는 객체를 인스턴스 라고 한다.
const javascript = new Object();
javascript.string = 'String';
javascript.function = function () {};
console.log(javascript); // {string: "String", function: f}
javascript 는 Object 생성자 함수 외에도 String, Number, Boolean, Function, Array, RegExp, Date, Math, Promise 등의 빌트인 생성자 함수를 제공한다.
const text = new String('text'); // String {"text"}
const number = new Number(5); // Number {5}
const boolean = new Boolean(false); // Boolean{false}
const function = new Function('a', 'return a'); // f anonymous (a)
const array = new Array(1, 2, 3); // [1,2,3]
반드시 생성자 함수를 통해 객체를 생성해야하는 것은 아니며 특별한 경우가 아니라면 리터럴로 객체를 생성하는 것 이 더 간편합니다.
생성자 함수
객체 리터럴을 통해 객체를 생성하는 방법은 편리하지만 중복되는 메서드와 프로퍼티를 매번 만들어주는 것은 유지보수에 비효율적이다.
이를 생성자 함수를 통해 프로퍼티 구조가 동일한 객체를 생성할 수 있다. (인스턴스를 생성하기 위한 클래스와 유사)
// 생성자 함수의 경우 일반함수와 구별하기위해 파스칼 케이스로 주로 선언함
function Test(text) {
console.log(this); // 일반 함수로 호출될 경우 this는 전역객체(window)를 생성자 함수로 호출될 경우 생성자 함수가 생성할 인스턴스를 가리킨다
this.text = text;
this.getFullText = function () {
return this.text + ' wow amazing';
};
}
const test = new Test(5);
console.log(test); // Test {text: 5, getFullText: ƒ}
const test1 = Test('!'); // 일반함수로 호출 되었기 때문에 전역객체 (window)에 text 프로퍼티와 getFullText 메서드가 생성되었다
console.log(text); // "!"
console.log(getFullText); // "! wow amazing";
this는 객체 자신의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수다. this 바인딩은 아래와 같이 호출방식에 따라 달라진다.
함수 호출 방식 | this 가 가리키는 것 (this 바인딩) |
---|---|
일반함수 | 전역객체 |
메서드 | 메서드를 호출한 객체 |
생성자함수 | 생성자 함수로 생성할 인스턴스 |
생성자 함수의 인스턴스 생성하는 과정
생성자 함수가 인스턴스를 생성하는 것은 필수이고, 생성된 인스턴스를 초기화 하는 것은 옵션이다.
-
인스턴스 생성과 this 바인딩
- 런타임 이전에 실행되며 암묵적으로 빈 객체가 생성되고 이 객체는 생성할 인스턴스이다. 이 인스턴스가 this에 바인딩 된다.
-
인스턴스 초기화
- 생성자 함수내의 코드가 한 줄씩 실행되어 this에 바인딩 되어있는 인스턴스를 초기화 한다.
-
인스턴스 반환
- 생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다.
-
주의
- this가 아닌 다른 객체를 명시적으로 반환하면 this가 반환되지 못하고 객체가 반환됨.
- 단, 명시적으로 반환되는 값이 원시 값일 경우 암묵적으로 this가 반환됨.
- 그러므로 생성자 함수 내부에선 return 문을 반드시 생략하는 것이 좋음.
내부 메서드 [[Call]] 과 [[Construct]]
함수는 객체이기 때문에 일반 객체와 동일하게 동작 할 수 있다. 일반 객체가 가지고 있는 내부 메서드와 내부 슬롯을 모두 가지고 있기 때문이다.
function js() {}
js.string = 'string';
js.function = function () {
console.log(this.string);
};
js.function(); // "string"
대신 함수는 일반 객체와는 다르게 호출 할 수 있다. 따라서 함수 객체는 함수가 동작하기 위한 [[Environment]], [[FormalParameters]] 등의 내부 슬롯과 [[Call]], [[Construct]] 같은 내부 메서드를 추가로 가지고 있따.
내부 메서드 [[Call]]을 갖는 함수 객체를 callable 이라 하며, 내부 메서드 [[Construct]]를 갖는 함수 객체를 constructor, 갖지 않는 객체를 non-constructor 라고 한다. 모든 함수객체를 callable 하며 constructor 일수도 있고 non-constructor 일수도 있다.
constructor와 non-constructor의 구분
- constructor: 함수 표현식, 함수 선언문, 클래스
- non-constructor: ES6 메서드 축약표현, 화살표 함수
아래의 두 함수 생성 방식은 non-constructor로 [[Construct]] 내부 메서드를 가지지 않아 new 연산자를 사용하여 생성자 함수로 호출 할 수 없다
const arrow = () => {};
const obj = {
get() {},
};
-
주의
- 생성자 함수로서 호출될 것 을 기대하고 정의하지 않은 일반함수에 new를 붙여 호출하면 생성자 함수처럼 의도와는다르게 동작 할 수 있음
new.target
ES6 부터 앞서 언급된 의도와는 다르게 생성자 함수가 일반함수처럼 호출되는 것을 방지하기위하여 new.target을 지원한다. new 연산자로 호출 시 함수 내부의 new.target은 생성자 함수로 호출될경우 함수 자기 자신을 가리키며 일반 함수로 호출할 시 undefined를 반환한다.
function Test(text) {
if (!new.target) {
return new Test(text); // 일반함수로 호출되었을 경우 new 연산자와 함께 재귀 호출하여 인스턴스를 생성한다.
}
this.text = text;
this.getFullText = function () {
return this.text + ' wow amazing';
};
}
// ES6 를 지원하지 않는 브라우저 에서는 스코프 세이프 생성자 패턴을 통해 대체 할 수 있다.
function Test(text) {
if (!(this instanceof Test)) {
return new Test(text);
}
this.text = text;
this.getFullText = function () {
return this.text + ' wow amazing';
};
}
Object, Function 함수의 경우 new 연산자 없이 호출하여도 new 연산자로 호출 하였을 때와 동일하게 동작한다. 그러나 String, Number, Boolean 함수는 new 연산자와 함께 호출 할 경우 String, Number, Boolean 객체를 생성하여 반환하지만 new 연산자 없이 호출 할 경우 문자열, 숫자, 불리언 값을 반환한다. 이를 통해 데이터 타입을 변환할 수 있다.