프로토타입과 Class

1. 프로토타입이란?

자바스크립트는 프로토타입 기반 언어로, 클래스 기반 언어와는 다른 방식으로 객체지향 프로그래밍을 구현한다.

프로토타입은 자바스크립트 객체가 다른 객체로부터 메서드와 속성을 상속받는 메커니즘이다.

 

모든 자바스크립트 객체는 프로토타입 객체에 대한 참조인 [[Prototype]]을 가지고 있다.

객체의 프로토타입은 Object.getPrototypeOf(obj) 또는 obj.__proto__로 접근할 수 있다.

자바스크립트에서 객체의 속성이나 메서드에 접근할 때, 해당 객체에 없으면 프로토타입 체인을 따라 올라가며 검색한다.

 

1.1. 객체지향 언어와 자바스크립트의 차이

전통적인 클래스 기반 언어(Java, C++ 등)에서는 클래스가 객체의 청사진 역할을 한다.

반면 자바스크립트는 클래스 개념 없이 객체가 다른 객체로부터 직접 상속받는 프로토타입 기반 언어이다.

 

클래스 기반 언어:

  • 클래스를 정의하고 인스턴스를 생성
  • 상속은 클래스 간에 발생
  • 컴파일 타임에 클래스 구조가 고정됨

 

프로토타입 기반 언어:

  • 객체를 직접 생성하고 확장
  • 객체가 다른 객체로부터 상속
  • 런타임에 객체 구조를 동적으로 변경 가능

 

ES6에서 class 키워드가 도입되었지만, 이것은 기존 프로토타입 상속에 대한 문법적 설탕(Syntactic sugar)일 뿐이다.

 

1.2. 프로토타입 체인

프로토타입 체인은 객체의 속성이나 메서드를 검색할 때 따라가는 참조의 사슬이다.

객체에서 속성을 찾지 못하면 해당 객체의 프로토타입에서 검색하고, 거기서도 찾지 못하면 프로토타입의 프로토타입에서 계속 검색한다.

이 과정은 프로토타입이 null인 객체(보통 Object.prototype)에 도달할 때까지 계속된다.

 

ex)

const animal = {
  eat: function() {
    return "먹는 중...";
  }
};

const dog = Object.create(animal);
dog.bark = function() {
  return "멍멍!";
};

console.log(dog.bark()); // "멍멍!" - dog 객체에서 직접 찾음
console.log(dog.eat());  // "먹는 중..." - 프로토타입 체인을 통해 animal에서 찾음
console.log(dog.fly);    // undefined - 프로토타입 체인 어디에도 없음

 

이 예시에서 dog 객체는 animal 객체를 프로토타입으로 가진다.

 

 

2. 프로토타입 상속

2.1. 프로토타입을 통한 메서드 상속

자바스크립트에서 프로토타입을 이용한 상속은 여러 방법으로 구현할 수 있다.

 

1) Object.create() 사용

Object.create()는 지정된 프로토타입 객체와 속성을 가진 새 객체를 생성한다.

 

ex)

const parent = {
  sayHello: function() {
    return "안녕하세요!";
  }
};

const child = Object.create(parent);
child.sayGoodbye = function() {
  return "안녕히 가세요!";
};

console.log(child.sayHello());    // "안녕하세요!" - 부모에서 상속
console.log(child.sayGoodbye());  // "안녕히 가세요!" - 자식의 메서드

 

2) 프로토타입 직접 설정

객체의 __proto__ 속성이나 Object.setPrototypeOf()를 사용하여 프로토타입을 직접 설정할 수 있다.

 

ex)

const parent = {
  sayHello: function() {
    return "안녕하세요!";
  }
};

const child = {
  sayGoodbye: function() {
    return "안녕히 가세요!";
  }
};

// 프로토타입 설정
Object.setPrototypeOf(child, parent);

console.log(child.sayHello());  // "안녕하세요!"

 

2.2. 생성자 함수와 프로토타입

자바스크립트에서 함수는 prototype 속성을 가지며, 이 속성은 new 키워드로 생성된 객체의 프로토타입이 된다.

 

ex)

// 생성자 함수
function Person(name) {
  this.name = name;
}

// 프로토타입에 메서드 추가
Person.prototype.sayHello = function() {
  return `안녕하세요, ${this.name}입니다.`;
};

// 인스턴스 생성
const person1 = new Person("김무무");
const person2 = new Person("김경민");

console.log(person1.sayHello());  // "안녕하세요, 김무무입니다."
console.log(person2.sayHello());  // "안녕하세요, 김경민입니다."

// 모든 인스턴스가 동일한 프로토타입을 공유
console.log(person1.__proto__ === Person.prototype);  // true
console.log(person2.__proto__ === Person.prototype);  // true

 

생성자 함수의 프로토타입에 메서드를 추가하면 해당 생성자로 만든 모든 인스턴스가 그 메서드를 공유한다.

 

 

3. Class

3.1. class 키워드의 내부 작동 방식

ES6에서 도입된 class 문법은 기존 프로토타입 상속을 더 직관적으로 작성할 수 있게 해준다.

그러나 내부적으로는 여전히 프로토타입 메커니즘을 사용한다.

 

ex)

// ES6 클래스 정의
class Person {
  constructor(name) {
    this.name = name;
  }

  sayHello() {
    return `안녕하세요, ${this.name}입니다.`;
  }
}

const person = new Person("무무");
console.log(person.sayHello());  // "안녕하세요, 무무입니다."

// 클래스 메서드는 프로토타입에 추가됨
console.log(person.sayHello === Person.prototype.sayHello);  // true

 

위 코드에서 sayHello 메서드는 Person.prototype에 추가된다.

constructor 메서드는 생성자 함수와 동일한 역할을 한다.

 

3.2. 클래스 vs 프로토타입 작성 비교

1) ES6 클래스를 사용한 상속

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    return `${this.name}가 소리를 냅니다.`;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);  // 부모 클래스의 constructor 호출
    this.breed = breed;
  }

  speak() {
    return `${this.name}가 멍멍 짖습니다.`;
  }

  describeSelf() {
    return `${this.name}는 ${this.breed} 입니다.`;
  }
}

const dog = new Dog("무무", "강아지");
console.log(dog.speak());        // "무무가 멍멍 짖습니다."
console.log(dog.describeSelf()); // "무무는 강아지입니다."

 

2) 프로토타입 방식

// 생성자 함수
function Animal(name) {
  this.name = name;
}

Animal.prototype.speak = function() {
  return `${this.name}가 소리를 냅니다.`;
};

function Dog(name, breed) {
  // 부모 생성자 호출
  Animal.call(this, name);
  this.breed = breed;
}

// 프로토타입 상속 설정
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;  // constructor 복구

// 메서드 오버라이드
Dog.prototype.speak = function() {
  return `${this.name}가 멍멍 짖습니다.`;
};

// 새 메서드 추가
Dog.prototype.describeSelf = function() {
  return `${this.name}는 ${this.breed} 입니다.`;
};

const dog = new Dog("무무", "강아지");
console.log(dog.speak());        // "무무가 멍멍 짖습니다."
console.log(dog.describeSelf()); // "무무는 강아지입니다."

 

클래스 문법은 상당히 간결하고 직관적인 반면, 전통적인 프로토타입 방식은 더 복잡하고 실수하기 쉽다.

그러나 두 방식 모두 내부적으로는 동일한 프로토타입 체인을 생성한다.

 

 

4. 주의사항 

1) 내장 객체 프로토타입 수정 자제

내장 객체(String, Array 등)의 프로토타입을 수정하는 것은 위험할 수 있다.

  • 다른 라이브러리와 충돌 가능성
  • 향후 자바스크립트 버전에서 동일한 이름의 메서드가 추가될 경우 문제 발생
  • 코드의 예측 가능성 감소

 

2) 프로토타입 체인의 깊이

프로토타입 체인이 너무 깊으면 속성 검색 시 성능이 저하될 수 있다.

// 프로토타입 체인이 너무 깊은 경우
const level1 = { prop1: 'value1' };
const level2 = Object.create(level1);
const level3 = Object.create(level2);
const level4 = Object.create(level3);
const level5 = Object.create(level4);

// level5.prop1에 접근할 때 5단계를 거쳐야 함
console.log(level5.prop1);  // "value1" (하지만 접근 속도가 느림)

 

3) 프로토타입 체인의 동적 수정

이미 생성된 객체의 프로토타입을 변경하면 성능에 악영향을 미칠 수 있다.

가능하면 객체 생성 시 프로토타입을 설정하고 이후에는 변경하지 않는 것이 좋다.

'학습 > JavaScript' 카테고리의 다른 글

JavaScript 비동기 3 : async/await  (0) 2025.04.11
JavaScript 비동기 2 : 콜백, Promise  (0) 2025.04.09
JavaScript 비동기 1 : 이벤트 기반  (0) 2025.04.09
var, let, const  (0) 2025.04.07
Javascript와 C++  (0) 2024.12.15