본문으로 건너뛰기

prototype

🧑🏻‍💻 객체지향 프로그래밍


JS는 프로토타입 기반의 객체지향 프로그래밍을 지원하는 프로그래밍 언어이다.

  • 그 이유는 JS의 원시 타입의 값을 제외한 나머지 값들은 모두 객체(함수, 배열, 정규 표현식 등)이면서, 프로토타입을 기반으로 객체를 생성하고 상속을 구현하기 때문이다.

  • 객체는 속성을 통해 여러 개의 값을 하나의 단위로 구성한 복합적인 자료 구조이다.

  • 객체지향 프로그래밍은 이러한 객체를 객체의 상태를 나타내는 상태 데이터(프로퍼티)와 프로퍼티를 조작할 수 있는 동작(메서드)을 하나의 논리적인 단위로 묶어 생각한다.

🧑🏻‍💻 상속과 프로토타입


JS는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다.

✅ 상속 전

메서드 중복 생성
// 생성자 함수
function Circle(radius) {
this.radius = radius;
this.getArea = function () {
return Math.PI * this.radius ** 2;
};
}

const circle = new Circle(1);
const circle2 = new Circle(2);

console.log(circlel.getArea === circle.getArea); // false

✅ 상속 후

메서드 중복 생성
// 생성자 함수
function Circle(radius) {
this.radius = radius;
}

Circle.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
};

const circlel = new Circle(1);
const circle2 = new Circle(2);

console. log(circle.getArea === circle.getArea); // true

prototype 객체


✅ 내부 슬롯 [[ Prototype ]]

  • 모든 객체는 [[ Prototype ]] 이라는 내부 슬롯을 가지며, 이 내부 슬롯의 값은 프로토타입의 참조다.

  • 객체 생성 방식에 따라 프로토타입이 결정되고 [[ Prototype ]] 에 저장된다.

    객체 리터럴로 생성된 객체 : Object.prototype
    생성자 함수로 생성된 객체 : 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체

✅ prototype

메서드 중복 생성
  • 모든 객체는 "하나의 prototype"(prototype = prototype 객체, 생성자 함수의 프로퍼티)을 갖는다. prototype은 생성자 함수.prototype(인스턴스)를 가리킨다.

  • 모든 생성자 함수가 생성할 인스턴스(생성자 함수.prototype)는 constructor 프로퍼티를 갖는다. constructor 프로퍼티는 prototype 프로퍼티로 생성자 함수를 가리킨다.

  • 즉 생성자 함수는 자신의 prototype 프로퍼티를 통해 인스턴스에 접근할 수 있으며, 인스턴스 또한 자신의 constructor 프로퍼티를 통해 생성자 함수에 접근할 수 있다.

  • 생성자 함수 호출용으로 사용하지 않는 일반 함수도 prototype 프로퍼티를 소유하고 있지만 인스턴스를 생성하지 않는 이상 의미가 없다.

✅ __proto__ 접근자

  • 모든 객체는 __proto__ 접근자 프로퍼티를 통해 자신의 프로토타입, [[ Prototype ]] 내부 슬롯이 가리키는 프로토타입에 간접적으로 접근할 수 있다.

  • __proto__ 접근자 프로퍼티는 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수 ([[ Get ]], [[ Set ]]) 프로퍼티 어트리뷰트로 구성된 프로퍼티다.

  • __proto__ 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아닌 Object.prototype의 프로퍼티로, 모든 객체는 상속을 통해 Object.prototype.__proto__ 를 사용할 수 있다.

  • __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유

    순환 참조하는 프로토타입 체인이 만들어지면 프로토타입 체인 종점이 존재하지 않기 때문에 프로토타입 체인에서 프로퍼티를 검색할 때 무한 루프에 빠진다. 이를 1차적으로 방지하기 위해 __proto__ 접근자 프로퍼티를 통해 프로토타입에 접근하도록 하고있다.

  • 모든 객체가 __proto__ 접근자 프로퍼티를 사용할 수 있는 것은 아니기 때문에 __proto__ 접근자 프로퍼티를 직접 사용하는 것은 좋지 않다. 프로토타입 참조를 취득할 때는 Object.prototypeOf 메서드를, 프로토타입을 교체하고 싶은 경우에는 Object.setPrototypeOf 메서드를 사용할 것을 권장한다.
// 생성자 함수
function Person(name) {
this.name = name;
}

const me = new Person('Lee');

console.log(Person.prototype === me.__proto__); // true

✅ prototype와 __proto__ 접근자

메서드 중복 생성

✏️ 공통점

  • 두 프로퍼티 모두 프로토타입을 참조한다.

✏️ 차이점

  • __proto__ 접근자 프로퍼티는 모든 객체가 소유하고 사용할 수 있지만, prototype 프로퍼티는 생성자 함수(constructor)만 소유하고 사용할 수 있다.

  • __proto__ 접근자 프로퍼티는 객체가 자신의 프로토타입에 접근 또는 교체하기 위해 사용합니다. 반면 prototype 프로퍼티는 생성자 함수가 자신이 생성할 인스턴스의 프로토타입을 할당하기위해 사용한다.


참고 자료

  • 모던 자바스크립트 Deep Dive (사진 출처)