JavaScript - 프로토타입(prototype)

dobby·2024년 11월 28일
0
post-thumbnail

프로토타입

JavaScript는 클래스라는 개념이 없다.
ES6에서 class가 도입되었지만, 이는 사실 함수이며 클래스 기반에 익숙한 프로그래머들의 편의를 위해 프로토타입 기반 패턴클래스 기반 패턴처럼 사용할 수 있도록 도입된 것이다.

그러니, 프로토타입 기반 모델을 클래스처럼 쓸 수 있도록 한 것이므로 JavaScript의 클래스는 syntatic sugar(문법적 설탕)이다.

즉, Javascript는 기존의 객체를 복사하여 (cloning) 새로운 객체를 생성하는 프로토타입 기반의 언어이다.
프로토타입 기반 언어는 객체 원형인 프로토타입을 이용하여 새로운 객체를 만들어낸다.
이렇게 생성된 객체 역시 또 다른 객체의 원형이 될 수 있다.


프로토타입은 크게 두 가지로 해석된다.

  • 프로토타입 객체를 참조하는 prototype 속성
  • 객체 멤버인 proto 속성이 참조하는 숨은 링크

자바스크립트에서 함수는 객체이다.
그러므로, 생성자로 사용될 함수도 객체이다.
객체는 프로퍼티를 가질 수 있는데, prototype이라는 프로퍼티는 그 용도가 약속되어 있는 특수한 프로퍼티로서 prototype에 저장된 속성들은 생성자를 통해서 객체가 만들어질 때 그 객체에 연결된다.

생성자 함수와 달리, 클래스에서 메소드는 자동으로 prototype의 메소드가 된다.
즉, 인스턴스끼리 같은 메소드를 공유한다.

클래스는 생성자 함수와 매우 유사하게 동작하지만, 클래스가 생성자 함수보다 엄격하며 더 많은 기능을 제공한다.


📌 ES6 전, Class가 없던 시절

자바스크립트에 클래스는 없었지만, 함수와 new를 통해 클래스를 비슷하게 흉내낼 수 있었다.

function Person() {
 this.eyes = 2;
 this.nose = 1;
}

var kim = new Person();
var park = new Person();

console.log(kim.eyes); // 2
console.log(kim.nose); // 1

console.log(park.eyes); // 2
console.log(park.nose); // 1

kim과 park는 eyes와 nose를 공통으로 가지고 있지만, 메모리에는 eyes와 nose가 두 개씩 총 4개가 할당된다.

생성자 함수는 객체를 여러 개 생성할 때 유용하게 쓸 수 있지만, 객체지향 원칙 중 하나인 상속을 구현할 수 없다.
그 이유는 생성자 함수에 의해 생성되는 모든 인스턴스가 메소드를 중복 생성하고 중복 소유하기 때문이다.

즉, 객체를 100개 만들면 200개의 변수가 메모리에 할당될 것이다.
이런 문제를 프로토타입 기반 상속으로 해결할 수 있다.

function Person() {
 ... 
}
 
Person.prototype.eyes = 2;
Person.prototype.nose = 1;
  
var kim = new Person();
var park = new Person();
  
console.log(kim.eyes); // 2
console.log(kim.nose); // 1

Person.prototype이라는 빈 Object가 어딘가에 존재하고, Person 함수로부터 생성된 객체인 kim, park은 어딘가에 존재하는 Object에 들어있는 값을 모두 쓸 수 있다.

즉, eyes와 nose를 어딘가에 있는 빈 공간에 넣어놓고 kim과 park가 공유해서 사용하는 것으로, Person.prototype으로부터 eyes와 nose를 상속받았음을 의미한다.


Prototype Link와 Prototype Object

자바스크립트에는 prototype linkprototype object라는 것이 존재한다.
이 둘을 통들어 prototype이라고 부른다.

📌 Prototype Object

객체는 언제나 함수로 생성된다.

function Person() {} // 함수
const personObject = new Person(); // 함수로 객체를 생성

personObject 객체는 Person이라는 함수로 생성된 객체이다.
이렇듯 언제나 객체는 함수에서 시작된다.
일반적으로 쓰는 객체 생성도 예외는 아니다.

const obj = {};
const obj.= new Object();

위의 두 코드는 동일하다.

함수가 정의될 때는 2가지 일이 동시에 이루어진다.

1. 해당 함수에 Constructor(생성자) 자격 부여

constructor 자격이 부여되면 new를 통해 객체를 만들 수 있게 되는데, 이것이 함수만 new 키워드를 사용할 수 있는 이유이다.
constructor가 아니면 new를 사용할 수 없다.


2. 해당 함수의 prototype object 생성 및 연결

함수를 정의하면 함수만 생성되는 것이 아니라, Prototype Object도 같이 생성된다.

함수를 정의하여 생성된 함수는 prototype이라는 속성을 통해 prototype object에 접근할 수 있다.

prototype object는 일반적인 객체와 같으며, 기본적인 속성으로 constructor__proto__를 가지고 있다.

prototype 속성으로 prototype object에 접근하면, constructor는 prototype object와 같이 생성되었던 함수를 가리키고 있다.
여기서 __proto__prototype link이다.

위의 예시였던 kim과 park은 Person 함수를 통해 생성되었으니 Person.prototype을 참조할 수 있게 되는 것이다.


kim 객체에는 eyes가 없는데 어떻게 사용이 가능한걸까?
kim에는 eyes라는 속성이 없는데도 kim.eyes를 실행하면 2라는 값을 참조하는 것을 볼 수 있다.

Prototype Object에 존재하는 eyes 속성을 참조하는 것인데, 어떻게 가능한걸까?
바로 kim이 가지고 있는 딱 하나의 속성 __proto__가 그것을 가능하게 해주는 열쇠다.

prototype 속성은 함수만 가지고 있던 것과는 달리, __proto__ 속성은 모든 객체가 빠짐없이 가지고 있는 속성이다.

__proto__는 객체가 생성될 때 조상이었던 함수의 Prototype Object를 가리킨다.
kim 객체는 Person 함수로부터 생성되었으니 Person 함수의 Prototype Object를 가리키고 있는 것이다.


📌 객체, 함수, Prototype Object의 관계

kim 객체가 eyes를 직접 들고있지 않기 때문에 eyes 속성을 찾을 때까지 상위 프로토타입을 탐색한다.
최상위인 ObjectPrototype Object 까지 도달했는데도 못 찾았을 경우, undefined를 리턴한다.

이렇게 __proto__ 속성을 통해 상위 프로토타입과 연결되어 있는 형태를 프로토타입 체인 이라고 한다.


📌 프로토타입 체인, 최상위는 Object

이런 프로토타입 체인 구조 때문에, 모든 객체는 Object의 자식이라고 불리고 Object Prototype Object에 있는 모든 속성을 사용할 수 있다.

한 가지 예로는 toString() 함수가 있다.

Object가 가지고 있는 모든 속성을 자식이 사용할 수 있다.


정리

  • 자바스크립트는 프로토타입 기반 객체지향 프로그래밍 언어이다.
  • 프로토타입은 원형이라는 뜻이다.
  • 함수 객체에는 프로토타입이라는 특수한 유형의 객체 프로퍼티(속성)가 존재한다. (객체 !== 프로퍼티)
  • prototype 프로퍼티를 통해 생성자 함수는 인스턴스에게 프로토타입 객체에 있는 데이터, 메소드를 상속한다.
  • 인스턴스 객체의 Key에 접근할 때, 해당 객체에 key가 없다면 그 다음으로 상위 프로토타입(원형) 속성에 key가 있는지 확인한다.
  • 없다면 더 상위의 프로토타입(부모)에서 찾는데, 이것을 프로토타입 체인이라고 한다.
profile
성장통을 겪고 있습니다.

0개의 댓글