프로토타입 기반 상속
프로토타입 기반 상속
개요
JavaScript는 클래스 기반이 아닌 프로토타입 기반 상속을 사용하는 언어입니다. 이는 객체 지향 프로그래밍의 다른 접근 방식으로, 모든 객체가 프로토타입(prototype)이라는 다른 객체를 참조하며, 해당 객체의 속성과 메서드를 상속받는 구조입니다. 프로토타입 기반 상속은 JavaScript의 핵심 개념 중 하나로, 동적 언어의 유연성을 극대화하는 동시에 복잡한 상속 구조를 가능하게 합니다.
프로토타입 기반 상속의 개념
프로토타입이란?
JavaScript의 모든 객체는 내부적으로 [[Prototype]]
이라는 숨겨진 참조를 가집니다. 이 참조는 다른 객체를 가리키며, 객체가 특정 속성이나 메서드를 찾을 수 없을 때 프로토타입 체인(prototype chain)을 따라 상위 객체를 탐색합니다.
예를 들어, 다음과 같은 코드를 살펴보겠습니다:
const animal = {
eats: true,
speak() {
console.log("동물 소리");
}
};
const dog = {
barks: true
};
// dog의 프로토타입을 animal으로 설정
Object.setPrototypeOf(dog, animal);
dog.speak(); // "동물 소리" 출력
dog
객체는 animal
을 프로토타입으로 가지므로 speak()
메서드를 직접 정의하지 않아도 호출할 수 있습니다.
프로토타입 체인
프로토타입은 체인 형태로 연결됩니다. 객체가 특정 속성을 찾을 때, 해당 객체 자체에 없으면 프로토타입 객체에서 찾고, 그 프로토타입의 프로토타입으로 계속 탐색합니다. 이 과정은 [Object.prototype](/doc/%EA%B8%B0%EC%88%A0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/JavaScript/Object.prototype)
까지 이어집니다.
예시:
const obj = {};
console.log(obj.toString()); // Object.prototype의 toString() 호출
obj
는 빈 객체이지만, Object.prototype
의 메서드를 사용할 수 있습니다.
생성자 함수와 프로토타입
생성자 함수의 프로토타입
생성자 함수를 사용할 때, 함수의 prototype
속성은 해당 함수로 생성된 모든 인스턴스의 프로토타입이 됩니다.
예시:
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`안녕, ${this.name}`);
};
const alice = new Person("Alice");
alice.greet(); // "안녕, Alice" 출력
[greet](/doc/%EA%B8%B0%EC%88%A0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/JavaScript/greet)()
메서드는 [Person](/doc/%EA%B8%B0%EC%88%A0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/JavaScript/Person).prototype
에 정의되었으므로 모든 Person
인스턴스가 공유합니다.
상속 구현
자식 생성자가 부모 생성자의 프로토타입을 상속하려면 다음과 같이 구현합니다:
function Employee(name, job) {
Person.call(this, name); // 부모 생성자 호출
this.job = job;
}
Employee.prototype = Object.create(Person.prototype); // 프로토타입 연결
Employee.prototype.constructor = Employee; // 생성자 참조 복구
const bob = new Employee("Bob", "개발자");
bob.greet(); // "안녕, Bob" 출력
Object.create() 메서드
ES5에서 도입된 Object.create(proto, [propertiesObject])
는 지정된 프로토타입을 가진 새 객체를 생성합니다. 생성자 함수 없이도 프로토타입 기반 상속을 구현할 수 있습니다.
const vehicle = {
move() {
console.log("움직이고 있습니다.");
}
};
const car = Object.create(vehicle);
car.drive = function() {
console.log("운전 중입니다.");
};
car.drive(); // "운전 중입니다."
car.move(); // vehicle의 move() 호출
클래스와 프로토타입 기반 상속의 차이
ES6에서 도입된 [class](/doc/%EA%B8%B0%EC%88%A0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/JavaScript/class)
문법은 프로토타입 기반 상속을 더 직관적으로 표현하는 문법적 설탕(syntactic sugar)입니다.
예시:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}가 소리를 냅니다.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name);
}
bark() {
console.log("멍멍!");
}
}
const dog = new Dog("바둑이");
dog.speak(); // "바둑이가 소리를 냅니다."
dog.bark(); // "멍멍!"
실용적인 예제와 활용
상속 계층 구조
// Vehicle 클래스
class Vehicle {
constructor(brand) {
this.brand = brand;
}
move() {
console.log(`${this.brand}가 움직입니다.`);
}
}
// Car 클래스 (Vehicle 상속)
class Car extends Vehicle {
constructor(brand, model) {
super(brand);
this.model = model;
}
drive() {
console.log(`${this.brand} ${this.model}를 운전합니다.`);
}
}
const myCar = new Car("현대", "아반떼");
myCar.move(); // "현대가 움직입니다."
myCar.drive(); // "현대 아반떼를 운전합니다."
메서드 오버라이딩
class Bird {
fly() {
console.log("날고 있습니다.");
}
}
class Penguin extends Bird {
fly() {
console.log("펭귄은 날 수 없습니다.");
}
}
const penguin = new Penguin();
penguin.fly(); // "펭귄은 날 수 없습니다."
주의사항과 일반적인 오해
1. 내장 객체의 프로토타입 수정
Object.prototype
이나 Array.prototype
을 수정하면 기존 코드와 충돌할 수 있으므로 추천하지 않습니다.
// 나쁜 예
Array.prototype.myMethod = function() {};
2. 프로토타입 체인의 길이
길어진 프로토타입 체인은 성능 저하를 유발할 수 있습니다.
// 최대한 체인을 짧게 유지
const a = {};
const b = Object.create(a);
const c = Object.create(b);
// c -> b -> a -> Object.prototype (체인이 길어짐)
3. [__proto__](/doc/%EA%B8%B0%EC%88%A0/%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D/JavaScript/__proto__)
의 사용
__proto__
는 과거 호환성을 위한 비표준 속성으로, 대신 Object.getPrototypeOf()
와 Object.setPrototypeOf()
를 사용해야 합니다.
참고 자료
관련 문서
이 문서는 AI 모델(qwen-3-235b-a22b)에 의해 생성된 콘텐츠입니다.
주의사항: AI가 생성한 내용은 부정확하거나 편향된 정보를 포함할 수 있습니다. 중요한 결정을 내리기 전에 반드시 신뢰할 수 있는 출처를 통해 정보를 확인하시기 바랍니다.