#JavaScript #Prototype

자바스크립트(JavaScript)는 흔히 프로토타입(prototype) 기반의 언어라고 불린다. 프로토타입은 직역하면 원형이라는 의미이다. 자바스크립트로 생성한 모든 객체는 원형, 즉, 프로토타입 객체와 함께 쌍을 이룬다. 이러한 구조를 활용하면 C++을 이용한 객체 지향 프로그래밍(OOP : Object-Oriented Programming)에서 중요한 개념 중의 하나인 상속(inheritance)을 자바스크립트에서도 손쉽게 구현할 수 있다. 자바스크립트에서는 이를 ‘프로토타입 체인(prototype chain)’ 이라고 부른다. 프로토타입 체인을 이해하기 위해서는 우선 프로토타입 객체에 대한 감을 잡을 필요가 있다.

객체의 생성

자바스크립트에서는 정의된 모든 함수(function)를 객체를 생성하기 위한 생성자로 활용할 수 있다. C++에서 클래스 이름과 동일한 이름을 가진 멤버 함수가 생성자로 지정되는 것과는 상당히 다른 부분이다. 자바스트립트에서는 기본적으로 Object()라는 함수를 제공하고 있으며, new라는 연산자를 이용하여 다음과 같이 객체를 생성할 수 있다.

var a = new Object();

또한, 이러한 방법말고도 객체 리터럴 표기법(Object literal notation)을 이용하여 객체를 생성할 수도 있다. JSON(JavaScript Object Notation)과 유사하지만 차이점이 존재한다. 객체 리터럴 표기법을 이용하여 다음과 같이 객체를 생성할 수 있으며, 바로 위의 코드와 동일하다.

var a = {}

기본 자료형(Primitive) vs 객체(Object)

자바스크립트의 자료형에는 null, undefined, boolean, number, string, symbol, object의 7가지가 존재한다. 앞의 6가지는 이름만으로 어떤 것을 의미하는지 명백히 파악할 수 있는 만큼 기본 자료형으로 분류된다. 기본 자료형이 아닌 모든 것들은 전부 object 타입, 즉, 객체이다. 앞서 살펴본 것처럼 JSON과 유사한 객체 리터럴 표기법으로 객체가 생성된다는 점으로 미루어 보아 객체는 키와 값의 쌍들로 구성된다고 어렴풋이 추측해 볼 수 있겠다. 다만, 키라는 용어 대신 속성(property)이라는 이름을 사용한다. 자바스크립트 객체는 속성과 값의 쌍으로 구성된다.

기본 자료형이 아닌 모든 것들은 객체이므로 함수도 객체이다. 따라서 앞에서 언급한 Object() 힘수도 객체이다. 객체로 객체를 만든 것이다. C나 C++을 주로 다뤘다면 충격적으로 다가오는 사실 중의 하나일 것 같다.

함수 객체

앞서 설명했듯이 객체는 프로퍼티와 값의 쌍으로 구성되며, 자바스크립트의 모든 자료형이 값에 대입될 수 있다. 따라서 객체인 함수, 즉, 함수 객체도 속성과 값의 쌍을 여럿 가질 수 있다. 함수 객체는 특별히 prototype 이라는 속성을 가지는데, 이는 해당 객체의 프로토타입 객체를 가리킨다. 앞서도 언급했둣이 자바스크립트의 모든 객체는 프로토타입 객체와 한 쌍을 이룬다. 즉, 아래와 같이 함수를 정의하는 순간,

function funcA () {}

object 타입인 함수 객체 funcA가 생성되며, 이 객체는 함수 객체의 특징 상 prototype이라는 속성을 갖는다. 특정 객체의 속성을 참조하려면 .을 이용하면 된다. 즉, 아래와 같이 함수 객체의 프로토타입 객체를 참조할 수 있다.

funcA.prototype

프로토타입 객체

함수 객체가 생성되는 순간에 프로토타입 객체가 함께 생성된다. 이는 prototype 속성으로 참조할 수 있다. 프로토타입 객체도 객체이므로 Object() 함수 또는 객체 리터럴 표기법으로 생성하는 것과 그 원리는 다를 바 없다. 다만, 일반적인 객체들과 다른 점은 개발자가 모르는 사이에 생성된다는 점이다. 따라서, 앞서 함수 객체 funcA를 생성하는 순간 아래의 코드가 암묵적으로 수행되는 것으로 이해해 볼 수 있다.

var p = new Object();
p.constructor = funcA;
funcA.prototype = p;
p.__proto__ = Object.prototype;

위 코드로 이해를 해보면, 함수 객체 funcA가 생성될 때 프로토타입 객체가 생성된다. 함수 객체 입장에서는 prototype이라는 속성으로 프로토타입 객체를 참조할 수 있다. 한편, 프로토타입 객체는 두 가지 속성, constructor__proto__를 가진다. 전자는 다시 역으로 함수 객체 funcA를 가리킨다. 여기까지가 함수 객체와 생성되는 프로토타입 객체의 생성 과정과 실체에 대한 내용이다.

프로토타입 체인

프로토타입 객체에 대하여 한 가지 설명하지 않은 것은, 프로토타입 객체가 가진 두 가지 속성 중 하나인 __proto__ 속성이다. 이 속성은 함수 객체를 제외한 나머지, 그러니까, 프로토타입 객체를 포함한 일반 객체들이 생성될 때, 생성의 원인이 되는 객체의 프로토타입을 참조하기 위한 속성이다. 바로 앞 예제에서 프로토타입 객체 생성의 ‘원인’인 객체는 함수 객체 Object()이므로, 바로 이 함수 객체와 쌍을 이루는 프로토타입 객체를 참조한다는 것이다. 결국, 다음의 등식은 참이다.

funcA.prototype.__proto__ === Object.prototype

앞서, 모든 함수는 일반 객체를 생성하기 위한 생성자로서 활용할 수 있다고 했다. 따라서 함수 객체 Object()를 이용해서 또다른 객체를 만들 수 있는 것처럼, 함수 객체 funcA()를 이용해서 다음과 같이 또다른 객체를 생성할 수 있다.

var objA = new funcA()

앞서 설명한 것처럼 일반 객체인 objA__proto__라는 속성을 가진다. 해당 객체의 생성은 함수 객체 funcA()이 원인이므로 다음의 등식은 참이다.

objA.__proto__ === funcA.prototype

두 등식을 나란히 놓고보면,

objA.__proto__ === funcA.prototype
funcA.prototype.__proto__ === Object.prototype

__proto__ 속성으로 인해 일반 객체를 시작으로 두 개의 프로토타입 객체까지 연달아 연결된 것이다. 바로 이것을 프로토타입 체인이라고 한다. 이러한 이유로 자바스크립트 체인을 이용하면 OOP의 중요한 개념 중의 하나인 상속을 구현할 수 있다. 같은 방법으로 objB 객체를 만든다면, ‘objA`와 다른 객체이지만 동일한 프로토타입 객체를 참조한다. 이러한 특징을 이용하면 프로토타입 객체에 임의의 속성을 생성하여 C++에서의 클래스 변수처럼 사용할 수 있다.