All Articles

JavaScript의 prototype

 Java와 같은 클래스 기반 언어에서는 Class를 통해 객체를 생성하는데, JavaScript는 Class가 없어 Function과 prototype을 통해 객체를 만든다. 이 때문에 자바스크립트는 프로토타입 기반 언어라고도 불린다.

자바스크립트에도 Class가 있다?

ECMA2015를 통해 Class라는 문법이 추가되었다. 이는 문법이 추가된 것일 뿐, JavaScript의 Class는 사실 함수(Function)이다. 객체는 언제나 함수로 생성된다. 어떻게 만들어지는지 보자.

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

const hyunsang = new Person();
console.log(hyunsang.eye); // 2

객체 리터럴도 마찬가지다. 아래 두 예시는 사실 같은 코드인데, 여기서 Object가 JavaScript에서 기본적으로 제공하는 함수이기 때문이다. 위 예시의 Person의 경우와 비슷한 걸 알 수 있다.

const obj1 = {};
const obj2 = new Object();

프로토타입(Prototype)

함수가 정의될 때, 아래 2가지 일이 일어난다.

  1. 함수에 Constructor(생성자) 자격 부여 : new를 통해 객체를 만들어 낼 수 있게 됨
  2. 함수에 대한 프로토타입 객체 생성 및 연결 : 함수에는 prototype이라는 property가 생기고, 프로토타입 객체에는 constructor가 생겨서 서로에게 직접 접근할 수 있게 됨

참고로 프로토타입 객체에 접근하는 방법은 2가지가 있다.

  • 함수에서 prototype이라는 property를 통해 접근하는 방법
  • 해당 함수를 통해 찍어낸 객체들에게서 __proto__(= Prototype Link)이라는 property를 통해 접근하는 방법

다시 한번 강조하지만, “함수에서는 prototype, 객체에서는 __proto__“라는 걸 기억해야 헷갈리지 않는다.

심지어 프로토타입 객체 또한 그 자체로 객체이기 때문에, 앞서 얘기한 constructor 외에 __proto__라는 property도 기본적으로 갖고 있게 된다. 이를 통해 등장하게 되는 개념이 프로토타입 체인이다.

프로토타입 체인(Prototype Chain)

현 스코프 내에서 값을 못찾을 경우 상위 스코프로 올라간다는 “스코프 체인”이라는 개념을 알 것이다. 이와 닮은 개념이다. (변수를 찾아 올라가는 게 ‘스코프 체인’, 프로퍼티를 찾아 올라가는 게 ‘프로토타입 체인’)
프로토타입 체인에서도 현 객체에서 property를 못찾으면 상위 프로토타입으로 계속해서 거슬러 올라가게 된다. 이렇게 프로토타입 체인을 형성해주는 게 __proto__ 때문이다. 일반 객체의 __proto__가 프로토타입 객체를 가리키고 있는데, 프로토타입 객체도 __proto__가 있으니 이를 통해 상위 프로토타입 객체로 넘어갈 수 있는 것이다.
정리하면, __proto__라고 하는 Prototype Link가 모여 마침내 Prototype Chain을 만들어내는 것이다. 예시를 보자.

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

const hyunsang = new Person();
console.log(hyunsang.eye); // 2

Person.prototype.mouth = 1;
console.log(hyunsang.mouth); // ?

여기서 hyunsang.mouth의 결과는 어떻게 될까? hyunsang이라는 객체는 mouth라는 property를 갖고 있지 않으니까 undefined일까?

아래에서 hyunsang 객체가 어떻게 되어있는지를 보자.

> console.log(hyunsang)
Person {eye: 2, nose: 1}
    eye: 2
    nose: 1
    __proto__:
        mouth: 1
        constructor: ƒ Person()
        __proto__: Object

hyunsang이라는 객체는 mouth라는 property를 갖고 있지 않다는 사실은 맞다. 하지만 __proto__라는 프로토타입 링크를 통해 간접적으로 가지고 있다. 따라서 프로토타입 체인을 따라 mouth를 탐색해서 console.log(hyunsang.mouth)의 결과는 undefined가 아닌 1이 되게 된다. 이와 마찬가지로, 모든 객체에서 toString method를 쓸 수 있는 이유도 프로토타입 체인의 최상위인 Object의 프로토타입 객체 내에 이미 정의된 method이기 때문이다!


[참고자료]
https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain https://medium.com/@bluesh55/javascript-prototype-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-f8e67c286b67