생성자 함수와 메서드
prototype?
객체
prototype에 대해 설명하려면 객체에 대해 알아야 합니다. 흔히 JavaScript에서 거의 모든 것이 객체라고 설명합니다.
JS의 요소는 객체와 원시 타입(primitive type)으로 나눌 수 있습니다.
[원시 타입]
- 문자열 (string)
- 숫자 (number)
- 불리언 (boolean)
- null
- undefined
- 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()
을 정의한다면, p1
과 p2
에서 동일한 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;
};
와 동일합니다. 하지만 객체 메서드를 사용하는 전자가 훨씬 직관적입니다. 재미있죠?