Prototype
JavaScriptPrototype?
자바스크립트는 프로토타입 기반 언어이다. 클래스 기반 언어에서는 ‘상속’을 사용하지만 프로토타입 기반 언어에서는 어떤 객체를 원형(prototype)으로 삼고 이를 복제(참조)함으로써 상속과 비슷한 효과를 얻는다.
constructor, prototype, instance
var instance = new Constructor();
위 그림의 윗변(실선)의 왼쪽 꼭짓점에는 Constructor
(생성자 함수)를, 오른쪽 꼭짓점에는 Constructor.prototype
이라는 프로퍼티를 위치시켰다. 왼쪽 꼭짓점으로부터 아래를 향한 화살표 중간에 new
가 있고, 화살표의 종점에는 instance
가 있다. 오른쪽 꼭짓점으로부터 아래를 향한 화살표의 종점에는 instance.__proto__
라는 프로퍼티가 있다.
위 도식의 흐름을 풀어내자면,
- 어떤 생성자 함수(constructor)를
new
연산자와 함께 호출하면 Constructor
에서 정의된 내용을 바탕으로 새로운 인스턴스(instance)가 생성된다.- 이 때
instance
에는__proto__
라는 프로퍼티가 자동으로 부여되는데, - 이 프로퍼티는
Constructor
의prototype
이라는 프로퍼티를 참조한다.
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
어떤 데이터의 __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)
코어 자바스크립트 (정재남)