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

Minju Kim·2022년 6월 7일
0

Deep Dive

목록 보기
11/23
post-thumbnail
post-custom-banner
1. 생성자 함수를 이용하여 객체를 생성하는 방식
2. 객체 리터럴 방식과 생성자 함수 이용 방식의 장단점

📕 17.1 Object 생성자 함수

😮 생성자 함수란?

new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수

😮 인스턴스란?

생성자 함수에 의해 생성된 객체

  • 자바스크립트는 Object 생성자 함수 이외에도 String, Number, Boolean, Function, Array, Date, RegExp, Promise등의 빌트인 생성자 함수를 제공한다.
const person = new Object();

//프로퍼티 추가
person.name ='Lee';
person.sayHello = function () {
  console.log(`Hi! My name is ${this.name}`);
};

📕 17.2 생성자 함수

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

단 하나의 객체만 생성하므로, 동일한 프로퍼티를 갖는 객체를 여러 개 생성해야 하는 경우 매번 다른 프로퍼티와 메서드를 기술해야 하기에 비효율적이다.

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

프로퍼티 구조가 동일한 객체 여러 개를 간편하게 생성할 수 있다.
마치 객체(인스턴스)를 생성하기 위한 템플릿(클래스)처럼 사용할 수 있기 때문이다.

// 생성자 함수
function Circle(radius){
  this.radius = radius;
  this.getDiameter = function (){
    return 2*this.radius;
  };
}

//인스턴스 생성
const circle1 = new Circle(5);
const circle2 = new Circle(10);

💡 클래스 기반 객체지향 언어와의 차이점?
생성자 함수는 이름 그대로 객체(인스턴스)를 생성하는 함수다. 하지만 자바와 같은 클래스 기반 객체지향 언어의 생성자와는 다르게 그 형식이 정해져 있는 것이 아니라, 일반 함수와 동일한 방법으로 생성자 함수를 정의하고, new 연산자와 함께 호출하면 해당 함수는 생성자 함수로 동작한다.

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

생성자 함수의 역할은 프로퍼티 구조가 동일한 인스턴스를 생성하기 위한 템플릿(클래스)로서 동작하여 인스턴스를 생성하는 것과 생성된 인스턴스를 초기화(인스턴스 프로퍼티 추가 및 초기값 할당)하는 것이다. 생성자 함수가 인스턴스를 생성하는 것ㄷ은 필수이고, 생성된 인스턴스를 초기화하는 것은 옵션이다.

1. 인스턴스 생성과 this 바인딩

  • 암묵적으로 빈 객체가 생성된다.
  • 암묵적으로 빈 객체, 즉 인스턴스는 this에 바인딩된다.
  • 이 처리는 함수 몸체의 코드가 한 줄씩 실행되는 런타임 이전에 실행된다.

2. 인스턴스 초기화

런타임에, 생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되어 this에 바인딩되어 있는 인스턴스를 초기화한다. 이 처리는 개발자가 기술한다.

3. 인스턴스 반환

생성자 함수 내부의 모든 처리가 끝나면 완성된 인스턴스가 바인딩된 this가 암묵적으로 반환된다. 이 때, 명시적으로 this가 아닌 다른 객체를 반환하면 this가 반환되지 못하고 return문에 명시한 객체가 반환된다.
> 하지만 명시적으로 원시값을 반환하면 원시 값 반환은 무시되고 this가 반환된다.

⛔️ 포인트는! 생성자 함수 내부에서 return 문을 반드시 생략해야 한다.
생성자 함수 내부에서 명시적으로 this가 아닌 다른 값을 반환하는 것은 생성자 함수의 기본 동작을 훼손하기 때문이다.

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

✔️ 함수 선언문 또는 함수 표현식으로 정의한 함수는 일반적인 함수로서 호출할 수도 있지만 new연산자와 함께 호출하여 생성자 함수로서 호출할 수도 있다.
✔️ 함수는 객체이므로 일반 객체와 동일하게 동작하며, 일반 객체가 가지고 있는내부 슬롯과 내무 메서드를 모두 가지고 있기 때문이다.
✔️ 그런데! 함수는 일반 객체이면서도 일반 객체와 다르다.

⛔️ 일반 객체는 호출할 수 없지만, 함수는 호출할 수 있다. 따라서 함수 객체는 일반 객체가 가지고 있는 내부 슬롯, 내부 메서드에 추가로 함수 객체만을 위한 내부슬롯인 [[Environment]], [[FormalParameters]]와 내부 메서드인 [[Call]], [[Construct]]를 추가로 가지고 있다.

함수가 일반함수로 호출되면 [[Call]]이 호출되고, new 연산자와 함께 생성자 함수로 호출되면 [[Construct]]가 호출된다.

Callable?

내부 메서드 [[Call]]을 갖는 함수 객체, 호출할 수 있는 객체

Constructor?

내부 메서드 [[Contruct]]를 갖는 함수 객체, 생성자 함수로서 호출할 수 있는 함수

Non-constructor?

내부 메서드 [[Constructor]]를 갖지 않는 함수 객체, 생성자 함수로서 호출할 수 없는 함수

결론

함수 객체는 모두 callable이며, contructor, non-contructor로 나뉜다.

💨 constructor vs non-constructor

자바스크립트 엔진은 함수 정의를 평가하여 함수 객체를 생성할 때 함수 정의 방식에 따라 함수를 constructor or non-constructor로 구분한다.

constructor : 함수 선언문, 함수 표현식, 클래스
non-constructor : 메서드(ES6 메서드 축약 표현), 화살표 함수
** 함수가 어디에 할당되어 있는지에 따라 판단하는 것이 아니라 함수의 정의 방식에 따라 constructor와 non-constructor를 구분한다.

✨ new 연산자

일반 함수와 생성자 함수에 특별한 형식적 차이는 없고 new 연산자와 함께 함수를 호출하면 해당 함수는 생성자 함수로 동작한다. [[Call]]이 호출되는 것이 아니라 [[Construct]]가 호출되는 것이다.

반대로 new 연산자 없이 생성자 함수를 호출하면 일반 함수로 호출된다. 다시 말해, 함수 객체의 내부 메서드 [[Construct]]가 호출되는 것이 아니라 [[Call]]이 호출된다.

이 때, this가 가리키는 값이 달라진다. new Circle일 때에 this 는 Circle을 가리키며, 그냥 Circle로 호출하면 this는 전역객체 window를 가리킨다.

⛔️ 생성자 함수는 파스칼 케이스로 명명하여 일반 함수와 구분해 줘야 한다.

❂ new.target

생성자 함수가 new 연산자 없이 호출되는 것을 방지하기 위해 파스칼 케이스를 사용한다 하더라도 실수가 있을 수 있다. 이러한 위험성을 회피하기 위해 나온 것이 new.target이다.

new 연산자와 함게 생성자 함수로서 호출되면 함수 내부의 new.target은 함수 자신을 가리킨다. new 연산자 없이 일반 함수로서 호출된 함수 내부의 new.target은 undefined이다.

//생성자 함수
function Circle(radius){
  // 이 함수가 new 연산자와 함께 호출되지 않았다면 new.target은 undefined이다.
  if(!new.target){
    //new 연산자와 함께 생성자 함수를 재귀 호출하여 생성된 인스턴스를 반환한다.
    return new Circle(radius);
  }
}

//new 연산자 없이 생성자 함수를 호출하여도 new.target을 통해 생성자 함수로서 호출한다.
const Circle = Circle(5);
profile
⚓ A smooth sea never made a skillful mariner
post-custom-banner

0개의 댓글