프라이D
프라이Develog(❁´◡`❁)
프라이D
전체 방문자
오늘
어제
  • ALL (378)
    • TDD, Cleancode with JavaScr.. (5)
    • 프로젝트 (32)
      • work (3)
      • 직접 만드는 기술 블로그 (2)
      • 데일리 옥션 (19)
      • 모락모락 (8)
    • Computer Science (1)
    • Algorithm & 자료구조 (94)
      • 알고리즘 w.JavaScript (53)
      • 자료구조 (5)
      • (인프런) 자바스크립트 알고리즘 문제풀이 (34)
    • JavaScript (45)
      • JavaScript (41)
      • 모던 자바스크립트 Deep Dive (4)
    • WEB (13)
    • 회고 (12)
    • TIL (109)
    • WIL (7)
    • Stacks (20)
      • React.js (6)
      • Next.js (1)
      • Redux (3)
      • Node.js (2)
      • GIT (2)
      • SAP (1)
    • 15일 메이킹 프로젝트 (15)
    • 이전 기록 (14)
    • ETC. (5)
    • ---------------2021 (6)
      • 내일배움단-웹개발 5주 (2)
      • 정보처리기사 (4)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록

공지사항

인기 글

태그

  • 스파르타코딩클럽
  • 코딩프로젝트
  • Til
  • 자바스크립트
  • 자바스크립트비트마스크
  • JavaScript
  • MySQL
  • 2023 인프콘 후기
  • 투포인터알고리즘
  • nomadcoders
  • 내일배움단
  • 코드스테이츠
  • 국비지원
  • vanilaJS
  • 비트마스크
  • 모던자바스크립트딥다이브
  • 자바스크립트알고리즘
  • 내일배움카드
  • 알고리즘
  • nomadcoder

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
프라이D

프라이Develog(❁´◡`❁)

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

[모던 자바스크립트 딥다이브] 17. 생성자 함수

2022. 6. 16. 15:18

모던 자바스크립트 Deep Dive 17장 내용을 발췌, 요약한 글 입니다.

17.1 Object 생성자 함수

  • 생성자 함수 : new 연산자와 함께 호출하여 객체(인스턴스)를 생성하는 함수.
  • 인스턴스 : 생성자 함수에 의해 생성된 객체.
  • 자바스크립트는 Object생성자 함수 이외에도 String, Number, Boolean, Function, Array, Date, RegExp, Promise등의 빌트인 생성자 함수 제공.
  • 특별한 이유가 없는 이상 객체 리터럴을 사용하는게 더 간편하다.
// 빈 객체 생성
const person = new Object();

// 프로퍼티, 메서드 추가 
person.name = 'Park';
person.sayHello = function() {
    return `Hi, ${this.name}`;
};

// 다른 생성자 함수 예시
const func = new Function('x', 'return x * x'); // Function 객체(함수) 생성 
const date = new Date(); //현재 시간

17.2 생성자 함수

[객체 리터럴 방식 vs 생성자 함수 방식]

  • 객체 리터럴로 생성하는 방식은 동일한 프로퍼티를 갖는 객체를 여러개 생성할 경우, 같은 코드를 중복해서 작성해야 하기 때문에 비효율적이다.
  • 객체는 프로퍼티를 통해 객체 고유의 상태 state 를 표현한다. 그리고 메서드를 통해 상태 데이터인 프로퍼티를 참조하고 조작하는 동작behavior 을 표현한다.
  • 따라서, 객체마다 프로퍼티 값은 다를 수 있지만 메서드는 내용이 동일한 경우가 일반적이다. 만약 이런 객체를 수십개 작성해야 한다면, 객체 리터럴 방식으로는 문제가 있다.
  • 생성자 함수에 의한 방식은, 생성자 함수로 객체 생성을 위한 템플릿(클래스)을 만들어 객체(인스턴스)를 생성하는 방식이다. 이를 이용해 프로퍼티 구조가 동일한 여러 객체를 간편하게 생성할 수 있다.
// 생성자 함수
function Circle(radius) {
    // 생성자 함수 내부의 this는 생성자 함수가 생성할 인스턴스를 가리킨다.
    this.radius = radius;
    this.getDiameter = function() {
        return 2 * this.radius;
    };
}

const circle1 = new Circle(5); // 반지름 5
const circle2 = new Circle(10);

circle1.getDiameter(); // 10
  • 자바스크립트의 생성자 함수는 다른 클래스 기반 객체지향 언어와 다르게, 일반 함수와 동일한 방법으로 정의한다. 단, 호출시 반드시 new연산자를 붙여야 한다. 이를 생략할 시 일반 함수로 동작한다.
  • (일반 함수로 호출된 인스턴스는 반환문이 없기 때문에, 암묵적으로 undefined를 리턴한다.)
  • (일반 함수로 호출된 인스턴스에서 this는 undefined 혹은 전역 객체를 가리킨다.)

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

function Circle(radius) {
    // 1. 암묵적으로 인스턴스(빈 객체)가 생성되고 this에 바인딩된다. 
    // 이 객체는 생성자 함수가 생성한 인스턴스이다.
    // 이로 인해 생성자 함수 내부 this가 인스턴스를 가리키는 것이다. 
    console.log(this); //Circle {}

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

    // 3. this(완성된 인스턴스가 바인딩되어 있음)가 암묵적으로 반환된다.

    // 이 때 리턴문을 사용해 다른 객체를 명시적으로 반환하면, 그 객체가 리턴된다. 
    // 따라서 생성자 함수 내부에서 return 문은 반드시 생략해야 한다.
    // 단 명시적으로 원시 값을 리턴하면 이는 무시된다.
    return {};
}

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

  • 함수 또한 객체이기 때문에 일반 객체가 가지고 있는 내부 슬롯과 내부 메서드를 가지고 있다. 단, 일반 객체와의 차이점은 함수는 “호출할 수 있다” 는 점이다.
  • 이 때문에 일반 객체의 내부 슬롯/내부 메서드 + 함수 객체만을 위한 내부 슬롯들과 [[Call]], [[Construct]]와 같은 내부 메서드를 갖는다.
  • 함수가 일반 함수로서 호출되면 내부 메서드 [[Call]] 이 호출되고, new 연산자와 함께 생성자 함수로서 호출되면 [[Construct]]가 호출된다.
  • 호출될 수 있는 함수 ([[Call]]을 가진 함수객체)를 callable, [[Construct]]를 갖는 함수 객체를 constructor 라고 한다. 또한 [[Construct]]를 갖지 않는 함수는 non - constructor 라고 한다.
  • 호출할 수 없는 객체는 함수가 아니기 때문에, 함수 객체는 반드시 callable 이어야 한다. 그리고 [[Construct]]를 갖는가 여부에 따라 constructor, non - constructor 을 나눈다.
function foo() {};

// callable, non - constructor
foo();

// callable, constructor( [[Construct]]를 가질 경우.)
new foo();

[constructor 와 non - constructor 구분]

자바스크립트 엔진은 함수의 정의 방식에 따라 이를 구분한다.

  • constructor : 함수 선언문, 함수 표현식, 클래스
  • non - constructor : 메서드(ES6 메서드 축약표현), 화살표 함수
  • 일반적으로 함수를 프로퍼티의 값으로 사용하면 메서드로 통칭한다. 그러나 ECMAScript 사양에서는, 메서드란 ES6 메서드 축약 표현만을 의미한다. 즉, 함수의 할당 위치가 아니라 함수의 정의 방식에 따라 구분된다.
  • 함수를 new 연산자와 함께 생성자 함수로서 호출하면, 내부에 [[Construct]]가 호출된다. non-constructor 인 함수 객체는 내부 메서드 [[Construct]]가 없기 때문에 에러가 발생하는 것이다.
////// constructor //////

// 함수 선언문, 함수 표현식 
function foo() {};
const bar = function () {}; 
// 일반 함수로 정의된 메서드. (메서드로 인정되지 않음)
const bax = {
    x: function () {};
};

////// non - constructor //////

// 화살표 함수
const arrow = () => {};
// ES6 메서드 축약형으로 정의된 메서드. (메서드로 인정)
const obj = {
    x() {}
};

[new 연산자]

  • 생성자 함수를 new연산자 없이 호출하면, [[Construct]] 가 호출되는 것이 아니라 [[Call]]이 호출된다.
    따라서 생성자 함수를 호출할때는 반드시 new 연산자를 붙여주어야 한다.

[new 연산자 없이 호출하는 것을 방지하는 방법 : 파스칼 케이스, new.target]

  • 생성자 함수의 이름을 첫글자가 대문자인 파스칼 케이스로 명명하여 일반 함수와 구분한다.
  • 이렇게 해도 new를 빠트리고 호출하는 오류가 생길 수 있는데, 이 때 사용할 수 있는 방법은 new.target이다.
  • new.target은 this와 유사하게 constructor인 함수 내부에서 암묵적인 지역 변수와 같이 사용되며 메타 프로퍼티라고도 한다. ES6에서 지원되는 속성이며, IE는 지원하지 않는다.
  • 함수 내부에서 new.target을 사용하면 이 함수가 new 연산자와 함께 호출되었는지 확인할 수 있다. new 연산자와 함께 호출되면 new.target은 함수 자신을 가리키고, new 연산자 없이 일반 함수로서 호출되었다면 new.target은 undefined이다.
function Circle(radius) {
    // 함수가 `new` 없이 호출되었다면 `new.target`은 `undefined` 이다. 
    if (!new.target) {
        // 이 조건에 해당한다면 다시 new를 붙여 재귀적으로 호출한다. 
        return new Circle(radius);
    }
    this.radius = radius;
    this.getDiameter = function() {
        return 2 * this.radius;
    };
}

const circle = Circle(5); // new 연산자 없이도 constructor로서 호출할 수 있음.
  • 대부분의 빌트인 생성자 함수는 new 연산자로 호출되었는지를 확인한 후 적절한 값을 리턴한다. 따라서 new 연산자 없이 호출해도, new 연산자로 호출했을 때와 동일하게 동작한다.
  • 그러나 String, Number, Boolean 생성자 함수는 new 연산자 있을때는 객체를 생성하여 반환하지만,new 없이 호출하면 문자열, 숫자, 불리언 값을 리턴한다. 이런 원리로 데이터 타입을 변환할 수 있는 것!
const str = String(123);
console.log(str, typeof str); // 123 string

SUMMARY

  • 생성자 함수로 객체를 생성하는 방식은, 주로 프로퍼티의 구조는 같고 내용만 다른 객체 인스턴스를 생성할 때 사용한다.
  • Object, Function 이외에 다양한 빌트인 함수를 제공하며, 이들은 new 연산자를 붙여 호출한다. * JS에서 생성자 함수는 다른 OOP 언어와 다르게 일반 함수와 동일한 방법으로 정의한다.
  • 인스턴스의 틀이 되어주는 생성자 함수를 작성한 후, new 를 붙여 호출하여 인스턴스를 생성한다.
  • 1. 런타임 이전에 빈 객체가 암묵적으로 생성되고, 이 객체가 생성자 함수가 생성한 인스턴스가 된다. 그리고 이 객체가 this에 바인딩 된다. 이로 인하여 생성자 함수 내부의 this가 인스턴스를 가리키는 것이다.
  • 2. this 에 바인딩되어있는 객체를 개발자가 작성한 코드로 초기화한다.
  • 3. this(완성된 인스턴스가 바인딩 되어 있는)를 암묵적으로 리턴한다. 이 때 명시적으로 다른 객체를 리턴하면 그 객체가 리턴되고, 리턴값이 명시적으로 원시값일 때는 무시된다. 따라서 생성자 함수에서는 return문을 쓰지 않는다.
  • 함수는 일반 객체의 내부 메서드 / 내부 슬롯 + 함수객체의 고유한 내부 메서드 / 내부 슬롯을 갖는다. 함수가 일반 함수로서 호출되면 [[Call]], 생성자 함수로서 호출되면 [[Construct]] 내부 메서드가 호출된다.
  • callable 이면서 [[Construct]] 가 있으면 constructor , [[Construct]] 가 없으면 non - constructor 이다.
  • JS 는 이 둘의 차이를 정의 방식으로 구별한다. 일반 함수 정의 방식 및 일반적인 메서드 정의 방식은 constructor, 화살표 함수와 ES6 축약형 표현으로 정의된 메서드는 non - constructor.
  • 생성자 함수를 new 없이 실행할 경우, 일반적인 함수처럼 실행되며 이를 방지하려면 1. 생성자 함수는 파스칼 케이스 컨벤션을 사용하여 명명한다. 2. ES6에서 지원하는 new.target을 사용한다.
  • new.target은 생성자 함수에서 new와 함께 호출된 함수이면 자기 자신을, 아닐 경우 undefined 를 가진다. 이를 활용해 new 사용 여부를 확인하고, new 없이 호출되도록 재귀적으로 생성자 함수를 호출할 수 있다.

 

저작자표시 (새창열림)

'JavaScript > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글

[모던 자바스크립트 딥다이브] 함수 선언문, 함수 표현식  (0) 2022.05.23
[모던 자바스크립트 딥다이브] switch문, while문  (0) 2022.02.22
[모던 자바스크립트 딥다이브] 4.변수  (0) 2022.02.02
    'JavaScript/모던 자바스크립트 Deep Dive' 카테고리의 다른 글
    • [모던 자바스크립트 딥다이브] 함수 선언문, 함수 표현식
    • [모던 자바스크립트 딥다이브] switch문, while문
    • [모던 자바스크립트 딥다이브] 4.변수
    프라이D
    프라이D
    틀린내용 정정 및 개선사항은 언제든지 댓글 달아주세요 :D

    티스토리툴바