프로토타입은 자바스크립트에서 상속을 구현하기 위해 만들어졌다.
function Square(x) {
this.x = x;
this.getArea = function () {
return this.x **2;
};
}
const s1 = new Square(5);
const s2 = new Square(6);
console.log(s1.getArea());//25
console.log(s2.getArea());//36
console.log(s1.getArea === s2.getArea);//false
Square의 인스턴스를 생성할 때마다 getArea 메서드를 생성하고, 모든 인스턴스가 getArea 메서드를 중복해서 소유하게 된다. 이는 퍼포먼스와 메모리 측면에서 좋지 않다.
프로토타입을 통해 다음과 같이 변경할 수 있다.
function Square(x) {
this.x = x;
}
Square.prototype.getArea = function () {
return this.x ** 2;
};
const s1 = new Square(5);
const s2 = new Square(6);
console.log(s1.getArea()); //25
console.log(s2.getArea()); //36
console.log(s1.getArea === s2.getArea); //true
Square 생성자 함수의 프로토타입에 getArea 메서드를 추가했다.
getArea메서드는 한번만 생성되고 Square 생성자 함수의 모든 인스턴스는 getArea를 상속받아 사용할 수 있다.
생성자 함수, 프로토타입, 객체는 세트라고 생각하면 된다. 자바스크립트에서 거의 모든 것은 객체다. 그리고 모든 객체는 하나의 프로토타입을 갖고, 모든 프로토타입은 하나의 생성자 함수를 갖는다.
모든 객체는 [[Prototype]]이라는 내부슬롯을 가진다. 객체가 만들어 질 때 prototype이 결정되고, 슬롯에 할당된다. 내부 슬롯에 직접 접근할 수 없는대신 __proto__
라는 접근자 프로퍼티를 통해 프로토타입에 접근할 수 있다. 프로토타입은 constructor 프로퍼티를 통해서 생성자 함수에 접근할 수 있다.
위 Square를 예시로 들면 다음과 같은 구조라고 할 수 있다.
Square {
protorype : Square.prototype
}
Square.prototype {
constructor : Square
}
s1 {
[[Prototype]] : Square.prototype을 가리킴(직접 접근불가)
__proto__: Square.prototype
}
직접 출력해보면 다음과 같다.
function Square(x) {
this.x = x;
}
Square.prototype.getArea = function () {
return this.x ** 2;
};
const s1 = new Square(5);
console.log(s1.__proto__);//{ getArea: [Function (anonymous)] }
console.log(s1.__proto__.constructor);//[Function: Square]
s1에서 __proto__를 통해 Square.prototype에 접근한다.
Square.prototype에서 constructor를 통해 Square 생성자 함수에 접근한다.
위에서 __proto__는 접근자 프로퍼티라고 했다. 그렇다면 __proto__는 어디서 온 것일까?
__proto__는 Object.prototype의 접근자 프로퍼티이다.
프로토타입의 프로토타입은 언제나 Object.prototype이다. 자바스크립트는 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 슬롯의 참조를 따라 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다. 이를 프로토타입 체인
이라고 한다.
위 예시에서는 다음과 같은 과정으로 검색이 이루어진다.