자바스크립트 프로토타입에 대한 이해#SaveMyanmar ✊🏾

최근에 자바스크립트 프로토타입에 대해 다시 공부해보는 기회가 있었다. 나는 분명히 몇 년전에 책을 통해 공부한 적이 있는데 이 생소한 느낌은 뭘까. 그래서 다시 머리속에 되새김을 하기위해 블로그에 기본 개념을 정리를 해보려 한다.

우선 프로토타입은 용어부터 뭔가 정리가 쉽지 않다. prototype, prototype object, [[prototype]] 비슷 하면서도 다른 용어들 때문에 혼란스럽다. 그래서 자료마다 조금씩 다르게 부르는것도 이런 혼란을 반영하는 것 같다. 용어부터 정리를 해보자.

용어설명

  • .prototype 프로퍼티 : 함수에 정의된 프로퍼티. 기본 세팅은 프로토타입 객체(Prototype Object)를 참조한다. 함수를 정의하면 파싱되는 시점에 프로토타입 객체와 함께 만들어진다.
  • Prototype Object : 함수가 만들어지면 함께 만들어지는 객체이다. 이것이 해당 함수의 표본(사전적 의미의 프로토타입) 또는 원형 이라고 부를 수 있는 존재이다.
  • [[Prototype]] : 명세에 따르면 자바스크립트 객체는 [[Prototype]]이라는 내부 프로퍼티가 있고 다른 객체를 참조하는 단순 레퍼런스로 사용한다. 프로토타입 링크라고 부르기도 한다. (이 문서에서도 프로토타입 링크라고 부르자) .__proto__ 와 동일한것처럼 말하는 경우도 있는데 엄밀히는 그렇지 않다.
  • .__proto__ : 브라우저에서 [[Prototype]]을 들여다볼 수 있도록 제공하는 기능. ES5까지는 비표준이다. 객체에 실제하는 프로퍼티는 아니다. 그리고 프로퍼티처럼 보이지만 실은 게터/세터에 가깝다. 던더 프로토(dunder proto)라고 불리기도 한다.

함수와 프로토타입 프로퍼티(.prototype)

모든 함수는 .prototype 프로퍼티를 가지고 있고 .prototype 프로퍼티는 함수의 표본(Prototype Object)을 참조한다. 예를 통해 보자.

함수 F 를 작성해보자.

function F(){}

함수는 파싱단계에서 내부적으로 다음과 같은 구조가 만들어진다.

그림1
  • 함수 F 에 prototype이라는 프로퍼터가 생성된다.
  • 함수 F의 .prototype 프로퍼티는 F Prototype Object 를 참조한다. ⇒ 함수 F의 원형은 저것이야 라고 가리키고 있는 의미라고 생각된다.
  • F Prototype Object가 생성되며 constructor와 [[Prototype]] 프로퍼티(프로토타입 링크)가 있다.
  • constructor는 함수 F를 참조한다.

객체 생성

객체는 함수를 통해서만 생성된다.

let obj = {};

어라, 이렇게도 만들어지는데? 사실은 내부적으로 let obj = new Object(); 와 동일하게 동작한다. 결국 위 obj 객체도 함수 Object() 를 통해 만들어지는 셈이다.

함수 F를 통해 객체를 만들어보자.

let f = new F();

이렇게 객체를 생성하면 어떻게 될까. 프로토타입의 관점에서 그림을 보자.

그림2. 점선은 프로토타입 링크. 검은색 화살표는 직접 참조.
  • 객체 f가 생성 된다.
  • f에 프로토타입 링크([[Prototype]])가 생성되며 F Prototype Object와 링크되어 있다. 이런 프로토타입 링크는 점선 화살표로 표현하였다.

프로토타입과 상속

프로토타입의 주요 기능은 상속이다. 위 그림2에서 만든 객체 f 에는 한번도 정의하지 않았지만 f.toString 이라던지 f.hasOwnProperty 등의 메소드를 사용할 수 있는것도 이 덕택이다. 어떻게 가능할까?

우선 그림2에는 나오지 않은 부분이 있는데 그것을 마저 그려보자.

그림3. 태초에 Object가 있었으니.

아까 모든 객체는 함수를 통해서만 만들어진다고 말했다.

  • F Prototype Object역시 객체이므로 역시 함수를 통해 만든것인데 바로 function Object라는 내부 함수를 통해서 만든것이다.
  • function Object 도함수이므로 다른 함수처럼 prototype 프로퍼티를 가지고 있고 Object Prototype Object 을 참조한다.
  • F Prototype Object은 결국 Object Prototype Object을 프로토타입 링크로 연결하고 있다.

결국 모든 프로토타입 링크는 최종적으로 최상위 프로토타입 객체인 Object Prototype Object 을 향하고 있는 것이다.

그러나 이런식의 상속은 객체지향 언어에서 사용하는 클래스 상속의 개념과는 다르다. 클래스 인스턴스(복사) 대신에 프로토타입 체인(링크)를 통해 구현이 된다. 어떤 책에서는 이런 구별을 위해 상속대신 위임이라고 표현하기도 한다.

체인

저 그림을 보더라도 객체 f 에 정의되어 있지 않은 f.hasOwnProperty 를 과연 어떻게 쓸 수 있게 된 건지 약간 설명이 부족하다.

자바스크립트는 객체의 프로퍼티를 참조할때 [[Get]] 이란 내부 함수가 호출되어 탐색을 시작한다. [[Get]]은 우선 f에 직속된 프로퍼티중에 hasOwnProperty 가 있는지 찾고 있으면 그것을 사용한다. 없으면 프로토타입 링크를 타고 F Prototype Object에서 찾고, 또 없으면 프로토타입 링크를 타고 Object Prototype Object 에서 찾는다.

위 그림의 경우 최상위 Object Prototype Object 에서 hasOwnProperty를 발견할 수 있게되고 그것을 사용하게 된다.

이런식으로 체인이 동작한다.

활용1

객체를 생성할때 직접 상속하고 싶은 프로퍼티를 만들어보자.

function F(){
return "hi";
}
F.prototype.say = "ho";
let f = new F();
console.log(f.say); // ho
그림4

객체 f에는 say라는 프로퍼티가 없지만 프로토타입 링크를 통해 F Prototype Object의 프로퍼티에서 say를 찾아내어 사용할 수 있다.(f.say)

활용2

function Car(name) {
this.name = name;
}
Car.prototype.getName = function(){
return this.name;
}
Car.prototype.getColor = function(){
return this.color;
}
function PaintingCar(name, color) {
this.color = color;
Car.apply(this, arguments);
}
PaintingCar.prototype = Object.create(Car.prototype);let myCar = new PaintingCar('coco', 'orange');
console.log(myCar.getName(), myCar.getColor());
그림5

PaintingCar.prototype = Obejct.create(Car.prototype); 이 구문 때문에 PaintingCar의 prototype은 새로운 객체(New PaintingCar Prototype Object) 를 참조한다. 이 새로운 객체는 Car Prototype Object에 연결이 되어있다. 이 새로운 객체는 생성자가 없다. (기존의 PaintingCar Prototype Object를 참조하는 곳은 이제 없다.)

결국 myCar는 Car Prototype Object까지 연결이 되어, getName, getColor 를 사용할 수 있다.

정리

  • 함수를 정의하면 프로토타입 프로퍼티와 프로토타입 객체가 만들어지고. 프로토타입 프로퍼티가 프로토타입 객체를 참조한다.
  • 함수를 통해 객체를 만들면 함수의 프로토타입 객체와 연결되는 프로토타입 링크가 만들어진다.
  • 프로토타입을 통해 상속을 구현할 수 있다.
  • 이 개념은 객체지향 언어의 클래스 상속과는 다르다.

이 글이 마음에 드셨다면 👏🏽👏🏽와 커피한잔 후원하기 (카카오페이), 이메일로 소식을 받아보고 싶으시다면 ✉️ 이메일 구독하기 해주세요. 그리고 저희 회사에서 프론트엔드를 포함한 개발자분들 채용중입니다.

Vue.js 관련 이야기들 | working at habitfactory.co | 커피한잔 후원하기 https://bit.ly/355PDlu | 이메일 구독하기 https://bit.ly/3ax32Fn

Vue.js 관련 이야기들 | working at habitfactory.co | 커피한잔 후원하기 https://bit.ly/355PDlu | 이메일 구독하기 https://bit.ly/3ax32Fn