🌳 자바스크립트 class의 뿌리 prototype

ECMAScript 2015에서 class라는 문법이 도입되고, 상속을 통해 사용하고있는 객체를 확장하는등의 작업을 할 수 있다.

class 확장객체 extends 루트객체 {
  constructor(props) {}
  // ...
}

Reactclass기반 컴포넌트들도 비슷한 원리일 것이다.

class CustomComponent extends React.Component {}

그렇다면, 기존의 자바스크립트에서 업데이트되면서 class문법이 추가되었다는 것인데, 이전의 자바스크립트에서는 객체간 상속이란것이 불가능하였을 까?

Javascript 상속의 뿌리 prototype

Javascript는 흔히 프로토타입 기반 언어라고 한다. 추가된 Javascriptclass 문법 또한 사실, 프로토타입 기반이라고 한다.

Javascript내의 모든 객체들은 메소드를 포함한 속성들을 상속받기 위해서 **프로토타입 객체(prototype object)**라는것을 갖는다고 한다.

상속되는 속성들은 해당 객체의 속성으로 바로 존재하는것이 아닌, 객체의 생성자의 prototype이라는 속성에 정의 됨

해당 객체를 통해, 상위 혹은 그보다 더 상위 객체로부터 속성들을 상속받을 수 있다고 한다.

이렇게 프로토타입 객체를 통해 속성들을 상속받는 줄기를 **프로토타입 체인(prototype chain)**이라고 한다.

프로토타입 객체와 프로토타입 체인

function Person(name, age) {
  this.name = name
  this.age = age
}

const me = new Person('상민', 27)
me.valueOf()

Person이라는 생성자 함수로 me라는 객체를 만들고, valueOf라는 메소드를 호출을 시켜보았다.

사실, valueOf라는 메소드는 object의 메소드이다. 따라서, 해당 메소드를 갖고있는 me객체는 존재하지 않는다는 에러를 반환해야된다고 생각하지만, 정상적으로 호출이 된다.

왜일까?

1
  1. me라는 객체에서 valueOf라는 속성으 탐색한다.
  2. 해당 속성이 존재하지 않아, me프로토타입 체인으로 연결된 프로토타입 객체에서 valueOf라는 속성을 탐색한다.

    이 때의 프로토타입 객체에는 Person생성자 함수가 존재함

  3. 마찬가지로 해당 프로토타입 객체 에도 존재하지 않아, 해당 프로토타입 객체프로토타입 체인으로 연결된 프로토타입 객체를 탐색함

    이 때의 프로토타입 객체에는 Object객체가 존재함

  4. Object에서 valueOf라는 메소드를 발견하고, 호출함

프로토타입 체인 - prototype chain

모든 객체들이 갖고있는 하나의 공통된 속성이 있다.

const obj = {}
console.log(obj)
2

[[Prototype]]라는 숨겨진 속성이다. 이 객체를 통해 참조할수 있는 다른 객체를 설명한다. 이러한 방식으로 참조할 수 있는 객체들이 [[Prototype]]이라는 숨겨진 속성으로 연결되어있는 것을 프로토타입 체인 이라고 한다.

위에서, valueOf라는 속성을 찾아갈 때 생성자 함수로 생성된 함수 또한 객체이므로 [[Prototype]] 숨김 속성을 갖고 있었고, 그 프로토타입 체인을 통해 상속하고있는 프로토타입 객체를 타고 가다, Object에서 해당 메소드를 발견한 것이다.

만약, 가장 끝의 프로토타입 객체에도 존재하지 않았다면 undefined를 반환하게 된다.

함수의 prototype

function Person(name, age) {
  this.name = name
  this.age = age
}

Person.prototype.type = 'person'
const me = new Person('상민', 27)
console.dir(me)
3

생성자 함수는 prototype속성을 사용하여 프로토타입 객체에 접근할 수 있다.

하지만, .prototype 을 통한 방법으로는 모든 객체가 아닌 위와 같은 생성자 함수 에서만 접근이 가능하다.

생성자함수.prototype을 통해 prototype 객체에 값을 추가할 수 있는듯 함.

결과적으로 해당 생성자 함수를 통해 생성된 객체는 __proto__를 통해 조회하였을 때, 자신을 생성한 생성자함수를 constructor로, .prototype으로 생성한 값 총 두개가 존재하는 상태

5

위에서 잠시 얘기된대로, 생성자함수를 통해 생성된 객체 혹은 일반 리터럴 객체 모두 __proto__를 통해 자신이 상속되어있는 프로토타입 객체를 확인할 수 있음.

4

생성자 함수를 정의하였을 때 두가지 반응이 일어난다.

  1. 기본 반응인 본인 자신이 정의됨
  2. 생성자 함수는 함수이기 때문에, prototype이라는 속성을 기본적으로 가지고 있다.

    이 속성에는 기본적으로 함수 자기 자신을 지칭하는 counstructor속성과, 자신을 통해 생성되는 객체의 [[Prototype]]으로 연결되는 prototype 객체를 지정해줄 수 있다. 이때, constructor의 값과 기존 상속해주는 프로토타입 객체를 유지하기 위해 .prototype을 아예 새로운 객체로 변경하기 보다는 속성을 추가해주는것이 좋다.

이렇게 정의된 생성자 함수로 생성된 객체는, 프로토타입 체인을 통해 자신을 생성한 생성자 함수의 프로토타입 객체에 접근할 수 있다.

결론

Javascript의 발전으로 class문법이 등장하긴 했지만, 이 또한 prototype기반의 문법이였다.

모든 객체들의 [[Prototype]]라는 속성은, prototype chain을 의미하였고, 이 연결을 통해 자신이 상속되고있는 prototype객체들에 접근할 수 있었다.

생성자 함수의 경우 prototpye속성을 통해 프로토타입 객체를 조회할 수 있었고, 이 속성의 내부에는 기본적으로 함수 자기자신을 지칭하는 consturctor가 존재하며, 다른 속성을 추가할 수 있었다.

생성자 함수를 통해 생성되는 객체, 다른 리터럴객체 등등은 __proto__을 통해 자신이 상속되어있는 프로토타입 객체를 확인할 수 있었으며 아래의 3가가 존재

  • 자신을 생성한 생성자함수를 constructor로서
  • 생성자 함수의 prototype.특정 값을 통해 생성된 상속받는 값 (추가 하였다면?)
  • 생성자 함수가 상속받고있는 prototype 객체

prototype object들도 마찬가지로 [[Prototype]](__proto__를 통해 조회)속성을 통한 prototype chain이 있었고, 해당 chaining을 타고 올라가며 찾고자 하는 속성을 탐색한다.

추가참고


@SangMin
👆 H'e'story

🚀GitHub