모던 자바스크립트 딥다이브, 면접과 함께 공부하기 - 17장 생성자 함수에 의한 객체 생성

지인·2023년 7월 24일
0

DeepDive

목록 보기
14/17
post-thumbnail

💡 아래 내용은 모던 자바스크립트 딥다이브를 공부하며 이해했던 내용을 다루고 있습니다. 혹시 틀렸거나 잘못된 정보가 있다면 알려주세요!

📌 17장 생성자 함수에 의한 객체 생성

17.1 Object 생성자 함수

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

let myObject = new Object();

17.2 생성자 함수

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

const circle = {
  raduis: 5
} // 객체 리터럴로 생성하는 방식

객체 리터럴을 사용한 객체 생성 방식은 매우 편리하고 가장 일반적으로 사용되는 방법이지만, 중복을 처리하기 어려운 점, 캡슐화가 불가능하다는 점, 프로토타입 상속이 복잡해진다는 단점이 있다.

왜 프로토타입 상속이 복잡해지나요?

JS에서는 '프로토타입'이라는 메커니즘으로 상속을 구현하는데, 객체 리터럴로 객체를 생성하는 경우, 객체의 프로토타입은 기본적으로 Object.prototype으로 설정됩니다. 따라서 다른 객체의 프로토타입을 상속하려면 추가적인 작업이 필요해지고, 이를 권장하지 않습니다.

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

생성자 함수로 객체를 만들면, 재사용성, 프로토타입 기반 상속, 캡슐화의 장점이 있습니다.

function Person(name) {
  this.name = name; // 재사용성
  let age = 0; // 캡슐화

  this.getAge = function() { // 캡슐화된 속성에 대한 접근자
    return age;
  }

  this.birthday = function() { // 캡슐화된 속성을 수정하는 메서드
    age++;
  }
}

Person.prototype.greet = function() { // 프로토타입 기반 상속
  console.log(`Hello, my name is ${this.name}`);
}

let person1 = new Person('John');
let person2 = new Person('Sarah');

person1.greet(); // "Hello, my name is John"
person2.greet(); // "Hello, my name is Sarah"

console.log(person1.getAge()); // 0
person1.birthday();
console.log(person1.getAge()); // 1

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

  1. 인스턴스 생성과 this 바인딩 : 먼저, 빈 객체가 생성됩니다. 이 객체는 생성자 함수가 생성한 인스턴스로, 생성자 함수의 프로토타입 객체를 상속받습니다. 그리고 이 newly 생성된 객체는 this 키워드에 바인딩됩니다. 따라서 생성자 함수 내부에서 this를 참조하면 생성된 인스턴스를 가리킵니다.
function Person(name) {
  console.log(this); // Person {}
  this.name = name;
}
  1. 인스턴스 초기화 : 생성자 함수에 기술되어 있는 코드가 한 줄씩 실행되면서 this에 바인딩된 인스턴스를 초기화합니다. 즉, 프로퍼티를 추가하거나 메소드를 생성하는 등의 작업을 수행합니다.
function Person(name) {
  // 인스턴스 초기화
  this.name = name;
}
  1. 인스턴스 반환 : 생성자 함수의 모든 코드가 실행되고 나면 this에 바인딩된 인스턴스가 암묵적으로 반환됩니다. 만약 생성자 함수가 명시적으로 객체를 반환하면 this가 아닌 해당 객체가 반환됩니다. 하지만 원시 값은 반환하면 무시되고 this에 바인딩된 객체가 반환됩니다.
function Person(name) {
  this.name = name;
  // this 반환
}

let john = new Person('John');
console.log(john); // Person { name: 'John' }

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

[[Call]] : 함수를 호출할 때 사용하는 내부 메서드입니다. 모든 함수 객체는 [[Call]] 내부 메서드를 가지고 있으며, 이 메서드가 있어야 함수로서 호출이 가능합니다. 예를 들어, myFunction()와 같이 함수를 호출하면 [[Call]] 내부 메서드가 실행됩니다.

function sayHello() {
    console.log('Hello!');
}

// 함수를 호출하면 내부적으로 [[Call]] 메서드가 동작합니다.
sayHello();  // 출력: Hello!

[[Construct]] : new 연산자와 함께 생성자 함수를 호출할 때 사용하는 내부 메서드입니다. 모든 생성자 함수는 [[Construct]] 내부 메서드를 가지고 있으며, 이 메서드가 있어야 new 키워드를 사용하여 인스턴스를 생성할 수 있습니다. 예를 들어, new MyConstructor()와 같이 생성자 함수를 호출하면 [[Construct]] 내부 메서드가 실행됩니다.

function Person(name, age) {
    this.name = name;
    this.age = age;
}

// new 키워드를 사용해 생성자 함수를 호출하면 내부적으로 [[Construct]] 메서드가 동작합니다.
var person = new Person('John', 30);

console.log(person.name);  // 출력: John
console.log(person.age);   // 출력: 30

17.2.5 constructor와 non-constructor의 구분

함수는 생성자(constructor)와 비생성자(non-constructor)로 구분되는데, 함수가 new 키워드를 사용하여 호출될 수 있는지 여부에 따라 결정됩니다.

생성자 함수(constructor)

생성자 함수는 new 키워드를 사용하여 인스턴스를 생성할 수 있는 함수입니다. 이 함수를 호출하면 this 바인딩을 통해 새 객체가 생성되고, 이 객체는 함수의 프로토타입을 상속받습니다. 생성자 함수는 일반적으로 첫 글자를 대문자로 작성하여 다른 개발자에게 생성자 함수임을 알립니다.

function Person(name) {
  this.name = name;
}

const john = new Person('John'); // 'new' 키워드를 사용하여 생성자 함수 호출
console.log(john.name); // 출력: 'John'

비생성자 함수(non-constructor)

비생성자 함수는 new 키워드와 함께 호출할 수 없습니다. 만약 new 키워드와 함께 호출하려고 시도하면, TypeError가 발생합니다. 화살표 함수, 메서드 축약 표현, Object, Function 등의 빌트인 함수는 비생성자 함수의 예시입니다.

const arrowFunc = () => {};
new arrowFunc(); // TypeError: arrowFunc is not a constructor

17.2.6 new 연산자

new 연산자는 객체를 생성하고 초기화하는 데 사용됩니다. 이 연산자는 생성자 함수를 호출하며, 그 과정에서 객체를 생성하고 반환합니다.

new 연산자를 실제 코드에 어떤식으로 사용하나요?

, 특정 데이터 구조를 나타내는 클래스나 생성자 함수를 정의하고, 그 구조에 맞는 다수의 객체를 만들어야 하는 경우에 new 연산자를 사용할 수 있습니다.

// 생성자 함수를 정의
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// new 연산자를 사용해 Person 객체를 생성
var person1 = new Person('John', 30);
var person2 = new Person('Jane', 25);

console.log(person1);  // Person { name: 'John', age: 30 }
console.log(person2);  // Person { name: 'Jane', age: 25 }

사용자가 정의한 클래스나 라이브러리가 제공하는 클래스를 인스턴스화할 때도 new 연산자가 사용됩니다. 예를 들어, Express.js와 같은 웹 프레임워크에서는 라우터 인스턴스를 만들기 위해 new를 사용합니다.

let express = require('express');
let router = new express.Router();

생성자 함수와 class 문법의 차이가 뭔가요?

// 생성자 함수
function Person(name, age) {
    this.name = name;
    this.age = age;
}

// 클래스
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

클래스는 ES6에 도입된 문법으로, 기존의 프로토타입 기반 객체 지향 패턴을 좀 더 명확하고 쉽게 사용할 수 있도록 제공된 문법입니다. 반면, 생성자 함수는 그 이전부터 존재하는 객체를 생성하는 방식입니다.

17.2.7 new.target

ES6에서 도입된 속성으로, 함수나 생성자가 new 연산자를 통해 호출되었는지 아닌지를 확인할 수 있습니다. new를 통해 호출된 함수 내부에서 new.target을 사용하면, new 연산자가 앞서 호출한 생성자를 참조합니다. 만약 new를 사용하지 않고 일반 함수처럼 호출했다면, new.target은 undefined를 반환합니다.

function ExampleConstructor() {
    if (!new.target) {
        console.log('Called without new');
    } else {
        console.log('Called with new');
    }
}

ExampleConstructor();  // 출력: 'Called without new'
new ExampleConstructor();  // 출력: 'Called with new'
profile
안녕하세요

0개의 댓글