JavaScript - Prototype - 1

Hunjin·2025년 4월 23일
post-thumbnail

자바스크립트의 Prototype에 대해 알아봅시다

자바스크립트는 명령형, 함수형, 프로토타입 기반, 객체지향 프로그래밍을 모두 지원하는 멀티 패러다임 언어입니다. 흔히 클래스 기반 언어인 Java나 C++과 비교하여 자바스크립트는 객체지향 언어가 아니라고 오해받는 경우가 있지만, 실제로 자바스크립트는 프로토타입 기반 객체지향 언어로, 클래스 없이도 상속과 코드 재사용이 가능합니다.

💡 코드 중복을 줄이기 위해 프로토타입을 사용합니다

// 생성자 함수
function Circle(radius) {
  this.radius = radius;
  this.getArea = function () {
    // Math.PI는 원주율을 나타내는 상수
    return Math.PI * this.radius ** 2;
  };
}

// 반지름이 1인 인스턴스 생성
const c1 = new Circle(1);
// 반지름이 2인 인스턴스 생성
const c2 = new Circle(2);

// Circle 생성자 함수는 인스턴스를 생성할 때마다 동일한 동작을 하는 
// getArea() 메서드를 새로 생성하고 모든 인스턴스가 중복 소유한다. 
// getArea() 메소드는 하나만 생성하여 모든 인스턴스가 공유해서 사용하는 것이 바람직하다. 
console.log(c1.getArea === c2.getArea); // false

getArea() 메서드는 모든 인스턴스에서 동일한 로직을 수행하지만,
각 인스턴스마다 별도의 함수로 생성되어 메모리를 낭비하게 됩니다.

✅ 프로토타입을 사용하면 어떻게 다를까요?

function Circle(radius) {
  this.radius = radius;
}
Circle.prototype.getArea = function () {
  return Math.PI * this.radius * this.radius;
};
// 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩되어 있습니다. 

// 반지름이 1인 인스턴스 생성
const c1 = new Circle(1);
// 반지름이 2인 인스턴스 생성
const c2 = new Circle(2);

Circle 생성자 함수가 생성한 모든 인스턴스는 자신의 프로토타입, 즉 상위 객체 역할을 하는 Circle.prototype의 모든 프로퍼티와 메서드를 상속받습니다.

getArea 메서드는 단 하나만 생성되어 프로토타입인 Circle.prototype의 메서드에 할당되어 있습니다. 따라서 Circle 생성자 함수가 생성하는 모든 인스턴스는 getArea 메서드를 상속받아 사용할 수 있습니다.

🧬 프로토타입은 어떻게 동작할까요?

인스턴스에서 어떤 프로퍼티나 메서드에 접근할 때, 자바스크립트는 다음과 같은 순서로 값을 찾습니다.

  1. 인스턴스 자체에 해당 프로퍼티가 있는지 확인
  2. 없다면 __proto__ (또는 내부 [[Prototype]])로 연결된 프로토타입 객체에서 탐색
  3. 계속 상위 프로토타입으로 따라가며 탐색 (이 과정을 프로토타입 체인이라고 부릅니다)
  4. 최종적으로 Object.prototype까지 가서도 없으면 undefined를 반환합니다

이런 구조 덕분에, 각 인스턴스는 자신의 상태(state)는 따로 관리하고,
공통 기능은 공유하여 사용할 수 있습니다.

🚀 proto 접근자 프로퍼티

모든 객체는 __proto__ 접근자 프로터티를 통해 자신의 프로토타입, 즉 [[prototype]] 내부 슬롯에 간접적으로 접근할 수 있습니다.


🚀 프로토타입의 constructor 프로퍼티와 생성자 함수

자바스크립트에서 모든 프로토타입 객체는 constructor라는 프로퍼티를 갖고 있습니다.
constructor는 말 그대로 "나를 만들어준 생성자 함수가 무엇이었는지"를 가리키는 역할을 합니다.

즉, 프로토타입은 자신을 참조하고 있는 생성자 함수를 기억하고 있습니다.

function Person(name) {
	this.name = name;
}

const me = new Person("Lee Hunjin");
// me 객체의 생성자 함수는 Person이다. 

console.log(me.constructor === Person) // true

✅ 무슨 의미일까요?

위 코드에서 mePerson이라는 생성자 함수를 통해 만들어진 객체입니다.
그런데 me 객체 자체에는 constructor라는 프로퍼티가 직접 정의되어 있지 않습니다.

그럼에도 불구하고 me.constructor를 호출할 수 있는 이유는,
me의 내부에는 [[Prototype]]이라는 숨겨진 링크(= proto)가 존재하고,
이 링크는 Person.prototype을 가리키고 있기 때문입니다.

me → Person.prototype → constructor → Person 함수

따라서 me.constructor를 호출하면 결국 Person.prototype.constructor를 참조하게 되고,
그 값은 Person 함수 자체가 되어 true가 출력되는 것입니다.

🚀 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입

앞서 살펴본 것처럼, 자바스크립트에서 어떤 객체가 생성되면 해당 객체는 자신을 만들어낸 생성자 함수와 연결됩니다. 이 연결은 객체의 프로토타입에 있는 constructor 프로퍼티를 통해 이루어지며, 이 프로퍼티는 "나를 만든 함수가 누구인지"를 가리킵니다.

// obj 객체를 생성한 생성자 함수는 Object()입니다.
const obj = new Object();
console.log(obj.constructor === Object); // true

여기서 obj.constructor는 Object를 가리킵니다.
즉, objObject 생성자 함수를 통해 만들어졌음을 알 수 있습니다.

// add 함수 객체를 생성한 생성자 함수는 Function()입니다.
const add = function (a, b) {
  return a + b;
};
console.log(add.constructor === Function); // true

add는 함수입니다. 그런데 자바스크립트에서는 함수도 객체이기 때문에, 이 함수 역시 어떤 생성자 함수로부터 만들어졌습니다. 그 생성자 함수가 바로 Function()입니다.

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

// Person 생성자를 통해 me라는 인스턴스를 생성
const me = new Person("훈진");

console.log(me.constructor === Person); // true

위 예제에서는 Person이라는 생성자 함수를 만들어 직접 인스턴스를 생성했습니다.
me 객체는 Person 생성자 함수를 통해 만들어졌고, me.constructor는 바로 그 Person을 가리킵니다.

✅ 정리하면

  • 자바스크립트에서 생성된 모든 객체는 자신을 생성한 생성자 함수의 정보를 갖고 있습니다.
  • 이 정보는 객체의 프로토타입 객체에 있는 constructor 프로퍼티를 통해 확인할 수 있습니다.
  • 즉, 객체프로토타입constructor생성자 함수 로 이어지는 구조입니다.

🚀 프로토타입의 생성 시점

// 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 생성
console.log(Person.prototype); // { constructor: ƒ Person(name) }

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

프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성됩니다. 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재합니다.

// 화살표 함수는 non-constructor이므로 생성자 함수로 사용할 수 없다.
const Person = (name) => {
  this.name = name;
};

console.log(Person.prototype); // undefined
  • 화살표 함수는 생성자 함수로 사용할 수 없다.
  • 그래서 prototypeundefined다.
  • this도 바인딩 안 된다. 그래서 객체 만들 목적이면 일반 함수 써야 한다.

⛓️ 프로토타입 체인이란?

자바스크립트는 클래스 기반 언어가 아니라 프로토타입 기반 언어입니다.
즉, 객체 간의 상속이 클래스가 아니라 객체에서 객체로 연결되며 이루어집니다.
이 연결 구조를 프로토타입 체인이라고 부릅니다.

function Person(name) {
  this.name = name;
}

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

const me = new Person('훈진');

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

여기서 mePerson 생성자 함수를 통해 만들어진 객체입니다.
즉, me의 내부 [[Prototype]](= proto)은 Person.prototype을 참조합니다.

🔗 프로토타입 체인 구조

me → Person.prototype → Object.prototype → null

각 단계에서 뭘 하는지?

  1. me는 자신이 가진 프로퍼티(name)를 직접 가지고 있음 → hasOwnProperty('name')true

  2. me.sayHello()를 호출하면?

    • me 객체에 sayHello가 없으니,
    • me.__proto__Person.prototype에서 찾음 → ✅ 있음
  3. me.hasOwnProperty()는?

    • me에 없음 → Person.prototype에도 없음 → Object.prototype에서 찾음 → ✅ 있음

이처럼 필요한 속성이나 메서드를 계속 위로 올라가며 찾는 구조가 바로 프로토타입 체인입니다.

profile
프론트 개발을 해보아요👨🏻‍💻

0개의 댓글