생성자 함수와 메서드

About JS, ES6
2023/10/19

prototype?

객체

prototype에 대해 설명하려면 객체에 대해 알아야 합니다. 흔히 JavaScript에서 거의 모든 것이 객체라고 설명합니다.

JS의 요소는 객체와 원시 타입(primitive type)으로 나눌 수 있습니다.

[원시 타입]

  1. 문자열 (string)
  2. 숫자 (number)
  3. 불리언 (boolean)
  4. null
  5. undefined
  6. symbol (ES6부터 도입된 고유 식별자)

위 타입을 제외한 JS 내의 모든 요소가 객체입니다. (함수, 배열, 정규표현식, 에러, etc)

const arr = [1, 2, 3]; // Array의 객체
const hanum = new Person(); // Person의 객체

JavaScript는 프로토타입 기반 언어

위 문구를 한 번쯤은 들어보셨을 겁니다.

모든 객체는 프로토타입 객체(prototype object)를 가지며 자신의 프로토타입 객체에 정의된 속성에 접근할 수 있다는 뜻입니다.

예를 들어 Array 객체의 프로토타입은 forEach(), map() 등의 메서드를 가지고 있기 때문에 [].forEach() 와 같은 형식으로 메서드를 사용할 수 있습니다.

생성자 함수를 사용할 경우, 아래와 같이 프로토타입 메서드를 정의합니다.

function Person(name) {
  this._name = name;
}
Person.prototype.getName = function () {
  return this._name;
};

const hanum = new Person("hanum");
hanum.getName();

프로토타입 객체에 메서드를 정의하는 것과 일반 메서드를 정의하는 것은 어떻게 다를까요?

prototype 객체는 인스턴스 내부에 존재하지 않습니다. 인스턴스 외부에 별도 객체에 저장되어 있죠.

퀴즈 문제처럼 일반 메서드를 이용한다면

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

const p1 = new Person("p1");
const p2 = new Person("p2");

p1, p2가 생성될 때마다 getName() 함수를 생성해야 하므로 자원이 낭비될 뿐더러, p1과 p2 내부에 각각 getName()대한 정보를 저장합니다. 기능은 같지만, 각 인스턴스에 새로운 메서드를 추가했을때와 동일하게 동작합니다. 아래처럼요.

p1.someFunc = () => {};
p2.otherFunc = () => {};

이는 메모리 관리에 비효율적입니다.

위의 prototype 설명의 예시처럼 prototype 객체에 getName()을 정의한다면, p1p2에서 동일한 prototype 객체의 getName()을 이용하게 됩니다.

객체가 가벼워지죠. 따라서 퀴즈 상황의 경우 prototype을 이용하는 것이 유리합니다.

그 외에도 일반 메서드가 아닌 프로토타입 메서드를 사용한다면

  • 프로토타입 체인을 통한 상속
  • 동적 업데이트 (일반 메서드의 경우 인스턴스 생성 이후 메서드 업데이트 불가능)
  • 코드 가독성 향상 (객체 property로 프로토타입 메서드가 표시되지 않음)

등의 장점이 있으니, 상황에 맞게 잘 사용하는 것이 좋습니다.

class 문을 사용한다면?

ES5에서 추가된 class문을 사용하면 훨씬 직관적으로 프로토타입 메서드를 작성할 수 있습니다. class문 또한 완전히 새로운 것이 아닌 프로토타입 기반의 syntax sugar이기 때문이죠.

class Person {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
}

사실 위의 코드는

class Person {
  constructor(name) {
    this.name = name;
  }
}
Person.prototype.getName = function () {
  return this.name;
};

와 동일합니다. 하지만 객체 메서드를 사용하는 전자가 훨씬 직관적입니다. 재미있죠?