Prototype

JavaScript

Prototype?

자바스크립트는 프로토타입 기반 언어이다. 클래스 기반 언어에서는 ‘상속’을 사용하지만 프로토타입 기반 언어에서는 어떤 객체를 원형(prototype)으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻는다.

constructor, prototype, instance

var instance = new Constructor();

prototype example

위 그림의 윗변(실선)의 왼쪽 꼭짓점에는 Constructor(생성자 함수)를, 오른쪽 꼭짓점에는 Constructor.prototype이라는 프로퍼티를 위치시켰다. 왼쪽 꼭짓점으로부터 아래를 향한 화살표 중간에 new가 있고, 화살표의 종점에는 instance가 있다. 오른쪽 꼭짓점으로부터 아래를 향한 화살표의 종점에는 instance.__proto__라는 프로퍼티가 있다.

위 도식의 흐름을 풀어내자면,

  1. 어떤 생성자 함수(constructor)를 new연산자와 함께 호출하면
  2. Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스(instance)가 생성된다.
  3. 이 때 instance에는 __proto__라는 프로퍼티가 자동으로 부여되는데,
  4. 이 프로퍼티는 Constructorprototype이라는 프로퍼티를 참조한다.

prototype은 객체이다. prototype객체 내부에는 인스턴스가 사용할 메서드와 프로퍼티를 저장한다. 위에 흐름을 따라가면, 생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 숨겨진 프로퍼티인 __proto__를 통해 이 메서드/프로퍼티들에 접근할 수 있게 된다는 걸 알 수 있다.

constructor 프로퍼티

생성자 함수의 프로퍼티인 prototype 객체 내부에는 constructor라는 프로퍼티가 있다. 인스턴스의 __proto__ 객체 내부에도 마찬가지이다. 이 프로퍼티는 단어 그대로 원래의 생성자 함수(자기 자신)를 참조한다. 인스턴스의 원형이 무엇인지 알 수 있는 수단으로 쓰인다.

var arr = [1, 2];
Array.prototype.constructor === Array // true
arr.__proto__.constructor === Array // true
arr.constructor === Array // true

var arr2 = new arr.constructor(3,4);
console.log(arr2); // [3, 4]

constructor는 읽기 전용 속성이 부여된 ‘기본형 리터럴 변수(number, string, boolean)’를 제외하고는 값을 바꿀 수 있다. 이 때에는 참조하는 대상이 변경될 뿐 이미 만들어진 인스턴스의 원형이 바뀐다거나 데이터 타입이 변하는 것은 아니기에, 어떤 인스턴스의 생성자 정보를 알아내기 위해 constructor 프로퍼티에 의존하는 것이 안전하지는 않다!

Prototype Chain

prototype chain example

어떤 데이터의 __proto__ 프로퍼티 내부에서 다시 __proto__ 프로퍼티가 연쇄적으로 이어진 것을 프로토타입 체인(Prototype Chain)이라 하고, 이 체인을 따라가며 검색하는 것을 프로토타입 체이닝(Prototype Chaining)이라고 한다.

프로토타입 체이닝은 메서드 오버라이드랑 동일한 맥락을 가진다. 어떤 메서드를 호출하면 자바스크립트 엔진은 데이터 자신의 프로퍼티들을 검색해서 원하는 메서드가 있으면 그 메서드를 실행하고, 없으면 __proto__를 검색해서 있으면 그메서드를 실행하고, 없으면 다시 __proto__를 검색해서 실행하는 식으로 진행한다.

var arr = [1, 2];
Array.prototype.toString.call(arr); // 1, 2
Object.prototype.toString.call(arr); // [object Array]
arr.toString(); // 1, 2

arr.toString = function(){
  return this.join('_');
};

arr.toString(); // 1_2

arr 변수는 배열이므로 arr.__proto__Array.prototype을 참조하고, Array.prototype은 객체이므로 Array.prototype.__proto__Object.prototype을 참조한다. toString이라는 이름을 가진 메서드는 Array.prototype 뿐 아니라 Object.prototype에도 있다. 4번째 줄에서 arr.toString을 실행했더니 결과가 Array.prototype.toString을 적용한 것과 동일하다. 6번째 줄에서는 arr에 직접 toString메서드를 부여했기 때문에 9번째 줄에서는 Array.prototype.toString이 아닌 arr.toString이 바로 실행된다.


출처
JavaScript: The Hard Parts (Will Sentance)
코어 자바스크립트 (정재남)