이해를 위한 기술블로그
All
All(11)
Javascript(2)
JavaScript의 prototype
Javascript
2025.08.23
thumbnail

자바스크립트에서 프로토타입은 객체 간의 상속을 구현하기 위해 사용되는 매커니즘입니다. 모든 자바스크립트 객체는 자신의 부모 역할을 하는 프로토타입 객체를 갖고 있으며, 이 프로토타입 객체로부터 속성과 메소드를 상속 받습니다.
이러한 상속 관계는 프로토타입 체인이라는 형태로 연결되어, 어떤 객체의 속성을 찾을 때, 해당 객체에 그 속성이 없으면 자바스크립트 엔진은 그 객체의 프로토타입으로 이동해서 찾습니다. 이 과정을 반복하며 체인의 끝인 null까지 탐색하며 여러 객체에서 공통된 속성이나 메소드를 공유할 수 있습니다.


프로토타입 기반 상속

자바스크립트는 클래스 기반 상속이 아닌 프로토타입 기반 상속을 채택하고 있습니다. 이는 객체를 생성할 때 새로운 클래스를 정의하는 대신, 기존 객체를 복제하거나 이를 프로토타입으로 삼아 새로운 객체를 만드는 방식입니다.
클래스 기반 상속 (C++, Java)등은 클래스라는 을 먼저 만들고, 그 틀을 기반으로 인스턴스를 생성해요. 이와 달리 프로토타입 기반 상속기존에 존재하는 객체를 기반으로 새로운 객체를 만들며 이 과정에서 프로토타입 체인 이라는 연결고리가 핵심적인 역할을 합니다.
ES6에서 도입된 class생성자 함수와 prototype 패턴을 깔금하게 감싼 문법이라고 할 수 있으며, 사실상 프로토 타입 기반 상속을 더 쉽게 사용할 수 있도록 할 뿐, 본질은 변하지 않습니다.

프로토타입 체인

모든 자바스크립트 객체는 [[Prototype]] 이라는 내부 슬록을 가집니다. 이 슬롯은 객체의 부모 역할을 하는 프로토타입 객체를 가리킵니다.

  • __proto__ (비표준): ECMAScript 명세에 포함된 공식적인 기능은 아니지만, 대부분의 브라우저에서 [[Prototype]]에 접근하기 위해 제공하는 비표준 속성. 객체의 프로토 타입을 확인하고, 다른 객체로 교체할 수 있습니다.
const animal = {
    isAnimal: true
}

const dog = {
    __proto__: animal
}

console.log(dog.isAnimal); // true
  • Object.getPrototypeOf() (표준): ES5부터 도입된 표준 API로, 객체의 프로토타입을 안전하게 가져올 수 있습니다.(읽기 전용)
const person = {
    isPerson: true
};

const student = {};
Object.setPrototypeOf(student, person); // ES6에서 프로토타입을 설정하는 표준 방식

console.log(Object.getPrototypeOf(student) === person); // true
console.log(student.isPerson); // true

어떤 객체의 속성에 접근할 때, 자바스크립트 엔진은 먼저 그 객체 자체에서 속성을 찾습니다. 만약 없으면 [[Prototype]]이 가리키는 프로토타입 객체로 이동해서 다시 찾습니다. 이 과정이 반복되면서 마치 사슬처럼 연결되는 것을 프로토타입 체인이라고 합니다. 이 체인의 끝은 null입니다.

내부슬롯 [[Prototype]]

자바스크립트의 내부 슬롯은 일반적인 객체 속성과는 다르게, 언어 명세(ECMAScript Specification)에서만 정의되는 개념적인 데이터 저장 공간입니다. 이는 개발자기 직접 접근하거나 조작할 수 없도록 설계된 숨겨진 정보입니다.

일반적인 객체 속성은 .(점) 표기법이나 [](대괄호) 표기법으로 접근할 수 있습니다. 예를 들어, obj.name이나 obj[name]처럼요. 하지만 [[Prototype]] 같은 내부 슬롯은 이런 방식으로 접근할 수 없습니다.

내부 슬롯의 역할은 객체의 동작 방식을 정의하고 관리하는 것입니다. [[Prototype]]은 객체가 어떤 프로토타입 객체를 상속받는지에 대한 정보를 저장하며, 이 정보를 기반으로 자바스크립트 엔진이 프로토타입 체인을 따라 속성을 탐색하는 동작을 수행합니다.

[[]]표기는 ECMAScript 명세에서 내부 슬롯을 나타내기 위해 사용하는 특별한 표기법입니다. 이는 해당 데이터가 일반적인 속성(property)이 아니라, 언어 내부적으로 정의된 숨겨진 매커니즘이라는 것을 명확히 구분하기 위함입니다.

함수와 프로토타입

자바스크립트에서 함수는 조금 특별한 역할을 합니다. 함수는 객체를 생성하는 생성자 함수(Constructor Function) 로 사용될 수 있으며, 이때 prototype 이라는 고유 속성을 가집니다. 이 prototype 속성은 생성될 객체들의 프로토타입 역할을 할 객체를 가리킵니다.

  1. 생성자 함수 자체
    일반 함수이지만 new 키워드로 호출될 때 특별한 동작을 합니다. 생성자 함수는 자신의 고유한 속성으로 prototype 속성을 가지며, 이 prototype 속성은 생성될 모든 인스턴스의 부모 역할을 할 객체(프로토타입 객체) 를 가리킵니다.

    생성자 함수도 결국 객체이기 때문에 [[Prototype]]을 가지며, Function.prototype을 가리킵니다. 따라서 Function.prptotype에 정의된 메소드 (call, apply, bind)를 상속받습니다.

  2. 생성자 함수로 생성된 객체 (인스턴스)
    [[Prototype]] 내부 슬롯을 가지며, 객체를 생성하는데 사용된 생성자 함수의 prototype 속성이 가리키는 객체를 참조합니다. 즉, new 키워드를 사용해 객체를 만들면, 인스턴스.__proto__ === 생성자함수.prototype 관계가 성립합니다.

    생성자함수.prototype.__proto__Object.prototype을 가리키며, 따라서 생성자 함수로 생성된 객체는 toString, hasOwnProperty 같은 공통 메소드를 상속 받습니다.

프로토타입 기반 상속 구현 예시

  1. 부모 객체 (생성자 함수) 정의
    부모 역할을 할 Animal 생성자 함수를 만듭니다. 공통 메소드는 메모리를 절약하기 위해 prototype에 정의합니다.
    function Animal(name) {
     this.name = name; // 모든 인스턴스가 가질 속성
    }
    // 모든 Animal 인스턴스가 공유할 메소드
    Animal.prototype.speak = function(){
     console.log(`${this.name}가 소리를 냅니다`);
    }
  2. 자식 객체 (생성자 함수) 정의
    자식 생성자 함수 Dog를 만들고 Animal의 속성을 물려받야아 흐므로, call메소드를 이용해 Animal 생성자 함수를 호출합니다.
    function Dog(name, breed) {
        Animal.call(this.name); // 상속: 부모 생성자 함수 호출
        this.breed = breed // Dog 고유의 속성
    }
  3. 프로토타입 연결 (상속 구현)
    DogAnimal의 메소드를 상속받기 위해서는 Dog.prototypeAnimal.prototype으로 연결해야 합니다. 가장 일반적인 방법 Object.create()를 사용합니다.
    // Dog의 프로토타입을 Animal의 프로토타입으로 설정하여 연결;
    Dog.prototype = Object.create(Animal.prototype);
    // 생성자 함수 연결 복원 (필수)
    Dog.prototype.constructor = Dog
  4. 자식 객체 고유의 메소드 추가
    Dog.prototype.bark = function() {
        console.log('멍멍')
    };
    
    // 부모의 메소드 재정의 (오버라이딩)
    Dog.prototype.speak = function() {
        console.log(`${this.name}가 멍멍 짖습니다.`);
    };
  5. 상속된 객체 사용
    이제 Dog 인스턴스를 만들고, Animal로부터 상속받은 속성과 메소드를 사용하는 것을 확인할 수 있습니다.
    const myDog = new Dog('바둑이', '진돗개');
    
    myDog.speak(); // '바둑이가 멍멍 짖습니다.' (오버라이딩된 메소드)
    myDog.bark(); // '멍멍!' (Dog 고유의 메소드)
    
    console.log(myDog.name); // '바둑이' (Animal로부터 상속받은 속성)

이러한 방식으로 자바스크립트는 클래스라는 명시적인 틀 없이도 유연하게 객체 간의 상속 관계를 구현합니다. ES6의 Class 문법은 이 복잡한 과정을 extends와 같은 키워드로 간단하게 만들어주는 문법일 뿐, 내부적으로는 여전히 생성자 함수프로토타입 기반의 상속 원리가 동작합니다.

프로토타입의 목적과 역할

  • 메모리 절약 및 성능 최적화 만약 수십 개의 객체에 동일한 메소드가 있다면, 클래스 기반 상속에서는 각 객체마다 해당 메소드를 복사하여 저장해야 합니다. 하지만 프로토타입을 이용하면 메소드를 프로토타입 객체에 한 번만 정의하고, 모든 인스턴스가 그 프로토타입 객체를 참조해서 사용하기 때문에 메모리를 효율적으로 관리할 수 있습니다.
  • 유연한 객체 확장 프로토타입은 객체의 속성과 메소드를 런타임에 동적으로 추가, 삭제, 변경할 수 있게 해줍니다. 이는 자바스크립트의 유연성을 극대화하는 중요한 특징입니다.

클래스 기반 언어에서도 메소드 복사로 인한 메모리 낭비를 줄이기 위한 다양한 최적화 기법이 존재합니다. 예를 들어, C++에서는 가상 함수 테이블(V-Table) 을 사용해 다형성을 구현하면서도 메소드 코드를 클래스마다 복사하지 않고 공유합니다. 즉, 클래스 기반 상속도 기술적으로는 프로토타입 기반과 유사한 메모리 공유 메커니즘을 적용하여 효율성을 높일 수 있습니다.


마무리

자바스크립트의 모든 객체는 궁극적으로 Object.prototype을 상속받습니다. 이는 Array, Date, Function 같은 내장 객체들도 마찬가지 입니다. 이들이 모두 Obejct.prototype에 정의된 toString(), hasOwnProperty() 같은 메소드를 사용할 수 있는 이유가 바로 프로토타입 체인 덕분입니다. 단순한 기술적 개념을 넘어, 언어의 근본적인 설계 철학을 담고 있으며, 이를 제대로 이해하면 객체 지향 프로그래밍을 더 깊이 있게 활용할 수 있고, 효율적인 코드를 작성할 수 있습니다.


© 2023 Developer Kim TaeHyun, Powered By Gatsby.