객체리터럴 방식으로 객체생성하는 방식 이외에도 다양한 방법으로 생성할 수 있다.
이번장 에서는 생성자 함수를 사용하여 객체를 생성하는 방식을 살펴본다.
17.1 Object 생성자 함수
new 연산자와 함께 Object 생성자 함수를 호출하면 빈객체를 생성하여 반환한다.
const person = new Object();
person.name = 'Lee';
person.sayHello = function () {
console.log("hello my name is " + this.name);
};
console.log(person); // { "name": "Lee" }
person.sayHello(); // hello my name is Lee
생성자 함수란 new 연산자와 함께 호출하여 객체를 생성하는 함수를 말한다.
자바스크립트는 Object 생성자 함수 이외에도 String, Number, Boolean, Function, Array, Date, RegExp, Promise 등의 빌트인 생성자 함수를 제공한다.
const strObj = new String('Lee');
const numObj = new Number(123);
const boolObj = new Boolean(true);
const func = new Function('x', 'return x * x');
const arr = new Array(1,2,3);
const regExp = new RegExp(/ab+c/i);
const data = new Date();
반드시 Object 생성자 함수를 사용해 빈객체를 생성해야 하는것은 아니다.
Object 생성자 함수를 사용해 객체를 생성하는 방식은 특별한 이유가 없다면 그다지 유용하지않다.
17.2 생성자 함수
17.2.1 객체 리터럴에 의한 객체 생성 방식의 문제점
객체리터럴에의한 객체생성방식은 직관적이고 간편하다.
const circle1 = {
radius : 5,
getDiameter() {
return 2 * this.radius;
}
}
console.log(circle1.getDiameter()); // 10
const circle2 = {
radius : 10,
getDiameter() {
return 2 * this.radius;
}
}
console.log(circle2.getDiameter()); // 20
객체는 프로퍼티를 통해 객체 고유의 상태를 표현한다.
그리고 메서드를 통해 상태 데이터인 프로퍼티를 참조해 동작을 표현한다.
객체 고유상태인 radius 프로퍼티의 값은 객체마다 다를 수 있지만 getDiameter 메서드는 완전히 동일하다.
하지만 객체 리터럴에 의해 객체를 생성해야 하는 경우 프로퍼티 구조가 동일함에도 매번 같은 프로퍼티와 메서드를 기술해야한다.
17.2.2 생성자 함수에 의한 객체 생성 방식의 장점
생성자 함수에 의한 객체 생성방식은 함수를 생성자함수를 사용하여 프로퍼티구조가 동일한 객체 여러개를 간편하게 생성할 수 있다.
function Circle(radius) {
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
}
}
// 인스턴스의 생성
const circle1 = new Circle(5);
const circle2 = new Circle(10);
// 일반함수 호출
const circle3 = Circle(14);
console.log(circle1.getDiameter()); // 10
console.log(circle2.getDiameter()); // 20
console.log(radius); // 14 , 일반함수로 호출되어 Circle 내의 this전역객체를 가리킨다.
생성자함수는 이름 그대로 객체를 생성하는 함수다.
new 연산자와 함께 호출하면 해당함수는 생성자 함수로 동작한다. 만약 new 연산자와 함께 생성자를 호출하지 않으면 생성자 함수가 아니라 일반함수로 동작한다.
17.2.3 생성자 함수의 인스턴스 생성과정
생성자 함수의 역활은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿으로서 인스턴스를 생성하는것과 생성된 인스턴스를 초기화하는 것이다.
function Circle(radius) {
this.radius = radius;
this.getDiameter = function() {
return 2 * this.radius;
}
}
const circle1 = new Circle(5); // 반지름이 5인 Circle 객체를 생성
new 연산자와함께 생성자함수를 호출하면 자바스크립트 엔진은 다음과 같은 과정을 거쳐 암묵적으로
인스턴스를 생성하고 인스턴스를 초기화한 후 암묵적으로 인스턴스를 반환한다.
1. 인스턴스 생성과 this 바인딩
암묵적으로 빈객체가 생성된다. 이 빈객체가 바로 생성자 함수가 생성한 인스턴스다.
그리고 암묵적으로 생성된 빈객체, 인스턴스는 this에 바인딩된다. 런타임이전에 실행된다.
function Circle(radius) {
// 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
console.log(this); // Circle {}
}
2. 인스턴스 초기화
생성자 함수에 기술되어있는 코드가 한줄씩 실행되어 this에 바인딩되어있는 인스턴스를 초기화한다.
function Circle(radius) {
// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩된다.
// 2. this에 바인딩되어 있는 인스턴스를 초기화한다.
this.radius = radius;
this.getDiameter = function () {
return 2 * this.radius;
};
}
필자는 인스턴스가 도대체무엇일까하는 의문이들어서 인스턴스를 다시 찾아보았다.
인스턴스는 객체이다. 즉 생성자함수에서 처음 Circle {} 이라는 객체가 자동생성되고 this에 바인딩된후
Circle{}.radius = radius ? 이런 형태로 객체(인스턴스를 초기화) 한다라고 이해하였다.
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}
17.2.4 내부 메서드 [[call]] 과 [[Construct]]
일반 객체는 호출할 수 없지만 함수는 호출할 수 있다.
따라서 함수객체는 일반 객체가 가지고 있는 내부 메서드는 물론, 함수가 동작하기 위해 함수객체만을 위한
[[call]] 과 [[Construct]] 같은 내부 메서드를 추가로 가지고있다.
function foo() {}
foo(); // 일반적인 함수로서 호출: [[Call]]이 호출된다.
new foo(); // 생성자 함수로서 호출: [[Construct]]가 호출된다.
[[Call]] 을갖는 함수객체를 callable이라 하며
[[Construct]]를 갖는 함수 객체를 constructor
[[Construct]]를 갖지 않는 함수객체를 non-constructor라고 부른다.
함수객체는 반드시 callable이여야한다. 모든 함수객체는 내부 메서드 [[Call]]을 갖고있으므로 호출 할 수있다.
하지만 함수객체는 constructor일수도있고 non-constructor일수도 있다.
17.2.5 constructor와 non-constructor의 구분
자바스크립트 엔진이 어떻게 구분하는지 보면 함수 정의 방식에따라 constructor와 non-constructor를 구분한다.
constructor: 함수 선언문, 함수 표현식, 클래스
non-constructor : 메서드(ES6 메서드 축약표현), 화살표함수
function foo() {} // 일반함수정의 : 함수 선언문
const bar = function () {}; // 함수 표현식
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
const obj = {
x() {} // 메서드정의 : ES6의 메서드 축약 표현만 메서드 인정
}
new obj.x(); // TypeError: arrow is not a constructor
함수를 프로퍼티값으로 사용하면 일반적으로 메서드로 통칭한다.
하지만 메서드(ES6 메서드 축약표현), 화살표함수 같은 경우는 non-constructor로 인지된다.
내부 메서드 [[Contruct]]를 갖지 않기때문에 non-constructor인 함수 객체를 생성자 함수로서 호출하면 에러가 발생한다.
필자는 이글을 정리하면서 함수와 메서드의 표현 차이를 알고싶었다. 객체안에 프로퍼티로 정의 함수를 메서드라고 한다. 여러개 문법을 보고 넘어가자
const bigtop = {
sayHi : function() { // 함수 선언방법
console.log("hi");
}
}
const bigtop = {
sayHi : () => { // 화살표함수 선언방법
console.log("hi");
}
}
const bigtop = {
sayHi () { // 키와 function 키워드생략
console.log("hi");
}
}
const bigtop = {};
bigtop.sayHi = function () {
console.log("hi"); // 객체 정의하고 프로퍼티값 추가
};
bigtop.sayHi = () => {
console.log("hi"); // 화살표함수
17.2.6 new 연산자
function add(x, y) {
return x + y; // 생성자 함수로서 정의하지않은 일반함수
}
let inst = new add(); // new 연산자호출
console.log(inst); // {}
function createUser(name, role) {
return { name, role };
} // 객체를 반환하는 일반함수
inst = new createUser('Lee', 'admin');
console.log(inst); // {name: 'Lee", role: "admin"}
일반 함수와 생성자함수에 특별한 형식적 차이는 없다.
단,생성자함수는 함수객체의 내부 메서드 [[Call]]이 호출되는것이 아니라 [[Construct]] 가 호출된다.
참고로 대부분의 생성자함수 (Object, String, Number, Boolean, Function, Array, Date, RegExp, Promise) 등 new 연산자와 함께 호출되었는지 확인한 후 적절한 값을 반환한다.
여기보충 >>
'Book > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글
20장 strict mode (0) | 2024.11.12 |
---|---|
49장 Babel과 Webpack이용한 환경구축 (2) | 2024.10.30 |
18장. 함수와 일급객체 (0) | 2024.10.22 |
16장 프로퍼티 어트리뷰트 (0) | 2024.10.16 |
15장 let, const 키워드와 블록레벨 스코프 (1) | 2024.10.13 |