레슨 10 / 12·5개 토픽
프로토타입과 클래스
프로토타입 체인
JavaScript의 모든 객체는 숨겨진 [[Prototype]] 링크를 통해 다른 객체와 연결됩니다. 속성이나 메서드를 찾을 때 현재 객체에 없으면 프로토타입 체인을 따라 상위 객체에서 탐색합니다. 이것이 JavaScript 상속의 핵심 메커니즘입니다.
javascript
// Object.create()로 프로토타입 체인 구성
const animal = {
type: "동물",
speak() {
return this.name + "이(가) 소리를 냅니다.";
},
};
const dog = Object.create(animal);
dog.name = "바둑이";
dog.bark = function() {
return this.name + ": 멍멍!";
};
console.log(dog.bark()); // "바둑이: 멍멍!"
console.log(dog.speak()); // "바둑이이(가) 소리를 냅니다." (animal에서 상속)
console.log(dog.type); // "동물" (프로토타입 체인)
// 프로토타입 체인 확인
console.log(Object.getPrototypeOf(dog) === animal); // true
console.log(dog.hasOwnProperty("name")); // true
console.log(dog.hasOwnProperty("type")); // false (상속된 속성)생성자 함수와 prototype
javascript
// ES6 이전의 전통적인 생성자 패턴
function Person(name, age) {
this.name = name;
this.age = age;
}
// prototype에 메서드 추가 — 모든 인스턴스가 공유
Person.prototype.greet = function() {
return "안녕하세요, " + this.name + "입니다. (" + this.age + "세)";
};
Person.prototype.isAdult = function() {
return this.age >= 18;
};
const kim = new Person("김철수", 25);
const lee = new Person("이영희", 17);
console.log(kim.greet()); // "안녕하세요, 김철수입니다. (25세)"
console.log(lee.isAdult()); // false
// 같은 prototype 메서드를 공유
console.log(kim.greet === lee.greet); // trueES6 클래스 문법
ES6 클래스는 프로토타입 기반 패턴을 깔끔하게 감싼 문법적 설탕(syntactic sugar)입니다. constructor, 인스턴스 메서드, static 메서드, 접근자(getter/setter)를 정의할 수 있습니다.
javascript
class Product {
// private 필드 (# 접두사)
#discount;
constructor(name, price) {
this.name = name;
this.price = price;
this.#discount = 0;
}
// 인스턴스 메서드
getInfo() {
return this.name + " — " + this.finalPrice + "원";
}
// getter — 속성처럼 접근
get finalPrice() {
return this.price * (1 - this.#discount);
}
// setter
set discountRate(rate) {
if (rate < 0 || rate > 1) throw new Error("0~1 사이 값");
this.#discount = rate;
}
// static 메서드 — 인스턴스 없이 호출
static compare(a, b) {
return a.price - b.price;
}
}
const laptop = new Product("노트북", 1200000);
laptop.discountRate = 0.1;
console.log(laptop.getInfo()); // "노트북 — 1080000원"
console.log(laptop.finalPrice); // 1080000
const phone = new Product("스마트폰", 800000);
console.log(Product.compare(phone, laptop)); // -400000상속: extends와 super
javascript
class Shape {
constructor(color) {
this.color = color;
}
describe() {
return this.color + " " + this.constructor.name;
}
area() {
throw new Error("하위 클래스에서 구현하세요");
}
}
class Circle extends Shape {
constructor(color, radius) {
super(color); // 부모 constructor 호출 (필수)
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(color, width, height) {
super(color);
this.width = width;
this.height = height;
}
area() {
return this.width * this.height;
}
}
const circle = new Circle("빨간", 5);
console.log(circle.describe()); // "빨간 Circle"
console.log(circle.area()); // 78.539...
const rect = new Rectangle("파란", 4, 6);
console.log(rect.area()); // 24
// instanceof로 타입 확인
console.log(circle instanceof Circle); // true
console.log(circle instanceof Shape); // true
console.log(rect instanceof Circle); // false프로토타입 vs 클래스 비교
- •
class는 프로토타입의 문법적 설탕 — 내부적으로 동일하게 동작 - •
class는 호이스팅되지 않음 — 선언 전에 사용하면 ReferenceError - •
class내부는 자동으로 strict mode 적용 - •
static메서드는Constructor.method()로 직접 정의한 것과 동일 - •
#private 필드는 클래스 문법에서만 지원 (프로토타입 패턴에서는 클로저로 구현) - •
instanceof는 프로토타입 체인을 따라 확인 — 두 방식 모두 동일하게 동작
💡
실무에서는 가독성과 유지보수를 위해 ES6 클래스 문법을 사용하는 것이 권장됩니다. 다만 프로토타입 체인을 이해해야 디버깅이나 라이브러리 코드 분석이 수월합니다.