Web/모던 자바스크립트 Deep Dive

[모던 자바스크립트 Deep Dive] 17. 생성자 함수에 의한 객체 생성

동띵 2023. 9. 5. 10:50

Object 생성자 함수


  • 객체는 객체 리터럴 이외에도 다양한 방법으로 생성 가능
    • 그 중 하나가 생성자 함수
  • new 키워드를 사용하여 Object 생성자 함수를 호출하면 빈 객체를 생성하여 반환
    • 빈 객체 생성 후 프로퍼티나 메서드를 추가하여 객체 완성
    const person = new Object();
    
    person.name = 'Lee';
    person.sayHello = function() {
    	console.log(this.name);
    }
    
  • 생성자 함수에 의해 생기는 객체 = 인스턴스

생성자 함수


  • 자바스크립트 빌트인 생성자 함수
    • Object, String, Number, Boolean, Function, Array, Date, RegExp, Promise 등
    const strObj = new String('Lee');
    console.log(typeof strObj); // object
    console.log(strObj); // String {'Lee'}
    
    const numObj = new Number(123);
    console.log(typeof numObj); // object
    console.log(numObj); // Number {123}
    

객체 리터럴에 의한 객체 생성 방식의 문제점

  • 객체 리터럴을 사용하여 객체를 생성하면 직관적이고 간편 but 하나의 객체만 생성 ⇒ 동일한 프로퍼티를 갖는 객체 여러 개를 생성해야 하는 경우 매번 같은 프로퍼티를 기술해야해서 비효율적

생성자 함수에 의한 객체 생성 방식의 장점

  • 프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성 가능 ⇒ 객체를 생성하는 템플릿st
function Circle(radius) {
	this.radius = radius;
	this.getDiameter = function() {
		return 2 * this.radius;
	}
}

// 인스턴스의 생성
const circle1 = new Circle(5);
const circle2 = new Circle(10);
  • new 연산자와 함께 호출 시 생성자 함수로 동작하고, new 연산자가 없다면 일반 함수로 동작

생성자 함수의 인스턴스 생성 과정

  • 생성자 함수의 역할 : 인스턴스 생성, 생성된 인스턴스 초기화 (인스턴스 프로퍼티 추가 및 초깃값 할당)
    • 인스턴스 생성은 필수, 초기화는 옵션
  • 생성자 함수에 인스턴스를 생성하고 반환하는 코드가 없어도 자바스크립트 엔진이 암묵적인 처리를 통하여 인스턴스 생성 및 반환 ⇒ new 연산자 사용해서 생성자 함수

인스턴스 생성과 this 바인딩

  • new 연산자와 함께 생성자 함수 호출 시 빈 객체가 생성되고, **this에 바인딩** 됨 ⇒ 생성자 함수 내부의 this가 생성자 함수가 생성할 인스턴스를 가리키는 이유 (this 바인딩은 런타임 이전에 실행)

인스턴스 초기화

  • 생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스 초기화
function Circle(radius) {
	// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩 됨

	// 2. this에 바인딩되어 있는 인스턴스 초기화
	this.radius = radius;
	this.getDiameter = function() {
		return 2 * this.radius;
	}
}

인스턴스 반환

  • 생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환됨
function Circle(radius) {
	// 1. 암묵적으로 인스턴스가 생성되고 this에 바인딩 됨

	// 2. this에 바인딩되어 있는 인스턴스 초기화
	this.radius = radius;
	this.getDiameter = function() {
		return 2 * this.radius;
	}
	// 3. 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환
}

// 인스턴스의 생성 (Circle 생성자 함수가 암묵적으로 this 반환)
const circle1 = new Circle(5);
  • 만약 생성자 함수에서 명시적 객체를 반환한다면 this를 반환하지 않고 명시적 객체 반환하고, 원시값 반환시 원시값은 무시되고 this 반환 ⇒ 생성자 함수 내부에서 return 문 사용 X

내부 메서드 [[Call]]과 [[Construct]]

  • 함수는 객체이지만 일반 객체와 다르게 호출 가능 ⇒ 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드뿐만 아니라 [[Environment]], [[FormalParameters]] 등의 내부 슬롯, [[Call]], [[Construct]] 내부 메서드 가지고 있음
  • 생성자 함수가 일반 함수로서 호출되면 함수 객체의 내부 메서드 **[[Call]]**이, new 연산자와 함께 생성자 함수로서 호출되면 [[Construct]] 가 호출됨
    • 내부 메서드 **[[Call]]**을 갖는 함수 객체 ⇒ callable
    • **[[Construct]]**를 갖는 함수 객체 ⇒ constructor
    • **[[Construct]]**를 갖지 않는 함수 객체 ⇒ non-constructor
    ⇒ 함수는 무조건 호출할 수 있으므로 함수 객체는 반드시 callable이지만, 생성자 함수로서 동작 가능하면 constructor, 일반 함수로서만 동작 가능하다면 non-constructor

constructor과 non-constructor의 구분

  • 자바스크립트 엔진은 함수 객체를 생성할 때 정의 방식에 따라 constructor와 non-constructor을 구분
    • constructor : 함수 선언문, 함수 표현식, 클래스
    function() {}
    const bar = function() {};
    
    const baz = {
    	x: function() {} // 프로퍼티 값으로 할당된 것은 일반 함수
    }
    
    new foo() {};
    new bar() {};
    new baz.x();
    
    • non-constructor : 메서드 (ES6 메서드 축약 표현), 화살표 함수
      • 이 함수들을 new 연산자와 함께 호출 시 에러 발생
    const arrow = () => {};
    new arrow(); // TypeError: arrow is not a constructor
    
    const obj = {
    	x() {}
    };
    new obj(); // TypeError: arrow is not a constructor
    

new 연산자

  • new 연산자와 함께 함수 호출하면 해당 함수는 생성자 함수로 동작 ⇒ 함수 객체의 내부 메서드 [[Call]]이 아닌 **[[Construct]]**가 호출됨
  • new 연산자와 함께 호출되는 함수는 constructor이어야 함
  • 생성자 함수는 일반 함수와 형식적 차이가 없기때문에 생성자 함수는 일반적으로 첫 문자를 대문자로 기술하는 파스칼 케이스로 명명하여 일반 함수와 구별

new.target

  • 생성자 함수를 new 키워드 없이 호출한 경우에도 생성자 함수로서 호출할 수 있게 해줌
// 생성자 함수
function Circle(radius) {
	// 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target은 undefined
	if (!new.target) {
		return new Circle(radius)
	}
	this.radius = radius;
	this.getDiameter = function() {
		return 2 * this.radius;
	}
}

// new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로서 호출됨
const circle = Circle(5);
  • new 연산자와 함께 생성자 함수에 의해 생성된 객체는 프로토타입에 의해 생성자 함수와 연결됨 ⇒ 생성자 함수가 new 연산자와 함께 호출됐는지 확인 가능
  • 자바스크립트 빌트인 생성자 함수 중 Object와 Function은 new 연산자 없이 호출했을 때와 new 연산자와 함께 호출했을 때 모두 동일하게 동작
let obj = new Object();
console.log(obj); // {}

let obj = Object();
console.log(obj); // {}

let f = new Function('x', 'return x ** x');
console.log(f); // f anonymous(x) {return x ** x}

let f = Function('x', 'return x ** x');
console.log(f); // f anonymous(x) {return x ** x}
  • 자바스크립트 빌트인 생성자 함수 중 String, Number, Boolean 생성자 함수는 new 연산자와 함께 호출하면 String, Number, Boolean 객체를 생성하여 반환하지만, new 연산자 없이 호출하면 문자열, 숫자, 불리언 값 반환
const str = String(123);
console.log(str, typeof str); // 123 string

const num = Number('123');
console.log(num, typeof num); // 123 number

const bool = Boolean('true');
console.log(bool, typeof bool); // true boolean