JavaScript의 prototype를 처음부터 다시 공부하면서 적어보았다. C/C++와 Java로 프로그래밍을 시작했고 오래써왔기 때문에 prototype을 이해하는데 꽤 오랜시간이 걸렸다. 둘 다 상속, 캡슐화, 추상화, 다형성 등을 전부 지원하지만 접근하는 방식은 좀 다를 뿐 깊게 들어가보면 동작하는 방식은 대동소이하다. Java와 비교하면서 적었기 때문에 Java를 쓰시는 분이면 이해하기 쉬울 듯하다.
Java Class와 JavaScript Prototype
Java Class
// Java
// class declaration
class Point {
private int x, y; // field
// constructor
public Point(int x, int y) {
this.x = x;
this.y = y;
}
// print method
public void print() {
// print Point in "(x, y)" format
System.out.println( "(" + this.x + ", " + this.y + ")" );
}
}
// new objects
Point p1 = new Point(0, 0);
Point p2 = new Point(5, 5);
Java는 class를 설계도삼아 object를 만들어낸다. 위의 코드에서는 Point class instance인 p1, p2가 생성된다. 메모리 관점에서 봤을 때 완전히 똑같은 object 두 개가 따로 메모리 상에 저장된다.
JavaScript Prototype
// JavaScript
function Point(x, y) {
this.x = x;
this.y = y;
}
// Point.prototype이 자동으로 생성
Point.prototype.print = function () {
console.log('(' + this.x + ',' + this.y +')');
}
// new instances
var p1 = new Point(0, 0);
var p2 = new Point(5, 5);
JavaScript 코드는 Java 코드와 비교하면서 한 줄 한 줄 설명해 해보았다.
Line 2 ~ 5
Java에서 class키워드를 사용해서 class를 선언하는데 비해서, JavaScript에서는 먼저 생성자(constructor)를 만든다. Point function이 선언됨과 동시에 Point function object가 생성된다 — JavaScript에서는 function도 object로 관리된다. Point function object의 내부에는 prototype이라는 property가 자동적으로 생성된다. 이게 바로 Point의 prototype이다. prototype은 메모리 상에 존재하는 object이며 거기서 생성/파생되는 모든 object의 원형이 된다. 기본으로 만들어 지는 prototype은 아무런 값이 없는 object이다 — 보든 JavaScript의 Object가 그렇듯 prototype object도 Object.prototype을 기반으로 한 object이다.
생성자인 Point function 에서는 일반적으로 property를 만들고 초기화하는 일을 한다. Java에서는 x, y field1 를 class 내에 따로 선언하지만 JavaScript는 따로 property1 선언은 하지 않고 생성자 내에서 this.x = x
처럼 값을 넣어서 선언 및 초기화 한다. 생성자에서 추가 된 property는 prototype과는 상관없이 각 object에 종속된다.
Line 8 ~ 10
print method를 Point의 prototype에 추가한다. 이것은 Java의 print method와 같은 역할을 하게 된다. prototype에 추가 된 method와 property는 이후에 생성된 모든 instance에서 접근이 가능하다.
Line 14 ~ 15
Line 14-15에서는 new키워드로 Point instance p1, p2를 생성한다. 내부적으로 동작하는 걸 자세히 따져보면 C/C++나 Java같은 instance화 라기보다는 prototype을 가리키는 빈 object를 만드는 것에 가깝다.
p1과 p2는 아주 단순한 구조를 가지고 있다. 빈 object에 x, y property를 가지고 있고, 거기에 추가로 숨겨진 property인 [[Prototype]]를 가진다 — Chrome, Safari 등 많은 모던브라우저에서는 __proto__라는 이름을 사용하고 있지만 ECMAScript 표준은 [[Prototype]]이다.
두 instance의 [[Prototype]]은 모두 Person.prototype를 가리킨다. 간단히 말해, p1과 p2는 하나의 prototype object를 공유한다. 이렇게 prototype object를 공유함으로써 Prototype Chain과 Property Shadowing이 가능해진다.
[1]: Java에서는 field, JavaScript에서는 property