JS-왜 프로토타입을 사용하나
| topics | 200-프론트개발 |
| types | 이론 |
| tags | #javascript #prototype #oop |
| references | medium.com/@limsungmook/%EC%9E%90%EB%... |
왜 prototype을 사용하는가?
자바스크립트는 왜 클래스 기반이 아니라 프로토타입 기반 언어로 만들어졌을까?
프로토타입의 실용적인 장점
1. 메모리 효율성
클래스로 객체를 만들 때, 메서드를 매번 복사하지 않고 prototype에 한 번만 정의하면 모든 인스턴스가 공유한다.
// 메모리 비효율적
function Dog(name) {
this.name = name;
this.bark = function() { // 인스턴스마다 복사됨
console.log(this.name + ' barks!');
};
}
// 메모리 효율적
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() { // 한 번만 정의, 모두 공유
console.log(this.name + ' barks!');
};
const dog1 = new Dog('멍멍이');
const dog2 = new Dog('바둑이');
console.log(dog1.bark === dog2.bark); // true - 같은 함수 참조
2. 동적 확장 가능
프로토타입은 런타임에 동적으로 변경할 수 있다. 이미 생성된 인스턴스에도 영향을 준다.
function Cat(name) {
this.name = name;
}
const cat1 = new Cat('나비');
// 나중에 메서드 추가 가능
Cat.prototype.meow = function() {
console.log(this.name + ' meows!');
};
cat1.meow(); // 이미 생성된 인스턴스에서도 사용 가능!
이건 클래스 기반 언어에서는 불가능하다. Java나 C++에서는 클래스 정의를 바꾸려면 재컴파일해야 한다.
3. 유연한 상속
프로토타입 체인을 이용하면 다중 상속처럼 동작하는 패턴을 만들 수 있다. (믹스인 패턴 참고)
const canEat = {
eat: function(food) {
console.log(`Eating ${food}`);
}
};
const canWalk = {
walk: function() {
console.log('Walking...');
}
};
function Person(name) {
this.name = name;
}
// 여러 기능을 조합
Object.assign(Person.prototype, canEat, canWalk);
const person = new Person('민지');
person.eat('pizza'); // Eating pizza
person.walk(); // Walking...
철학적 배경
왜 Brendan Eich는 프로토타입 방식을 선택했을까?
Class 기반 (플라톤)
눈앞에 실제로, 구체적으로 존재하는 사물이 있다면 반드시 그것의 본질이 존재한다
개념을 추상화하여서 정의하고, 인스턴스화하여 구체적으로 정의한다.
But, 현실에는 모든 것을 추상화하여 분류할 수 없는 경우가 존재한다.
Prototype 기반 (비트겐슈타인)
공유 속성의 관점에서 정의하기 어려운 개념이 있다.
세계에 미리 내재되어서 대상과 언어를 완전히 규정하는 어떤 언어란 존재하지 않는다.
표현은 삶의 흐름 속에서만 의미를 갖는다.
→ 맥락이 중요하다. → this 동적할당, 실행 컨텍스트...
→ 대상을 분류할 때 모집단에서 가장 유사성 높은 것 (가장 다른 것들과 비슷한 점이 많은 것)을 기준으로 잡는다. → prototype
언제 프로토타입을 직접 쓰나
요즘은 ES6 class 문법을 쓰지만, 내부적으로는 프로토타입으로 동작한다. (JS-클래스 참고)
직접 프로토타입을 쓰는 경우:
- polyfill 만들 때 - 내장 객체에 메서드 추가
- 라이브러리/프레임워크 개발 - 성능 최적화가 중요할 때
- 동적으로 메서드 추가/제거 - 런타임에 객체 동작을 변경해야 할 때
// polyfill 예시
if (!Array.prototype.includes) {
Array.prototype.includes = function(searchElement) {
// 구현...
};
}
주의: 내장 객체의 프로토타입을 함부로 수정하면 다른 코드와 충돌할 수 있다. 꼭 필요한 경우(polyfill)만 사용하자.
프로토타입 vs 클래스
| 프로토타입 | 클래스 | |
|---|---|---|
| 문법 | Dog.prototype.bark = ... |
class Dog { bark() {...} } |
| 가독성 | 낮음 | 높음 |
| 동적 수정 | 쉬움 | 어려움 |
| 성능 | 근소하게 빠름 | 근소하게 느림 |
| 사용 시기 | 라이브러리 개발, polyfill | 일반적인 애플리케이션 개발 |
결론: 일반적인 개발에서는 class 문법을 쓰자. 하지만 프로토타입의 동작 원리를 이해해야 디버깅이나 최적화할 때 도움이 된다.
관련 문서
- JS-prototype - 프로토타입 상세 설명
- JS-클래스 - class 문법과 프로토타입의 관계
- JS-this - this 바인딩과 실행 컨텍스트
- JS-믹스인 - 믹스인 패턴으로 다중 상속처럼 사용하기