자바스크립트는 원형 객체로 새로운 객체를 생성하는 프로토타입(원형) 기반 언어다. 프로토타입 기반 언어란 무엇일까? 자바스크립트로 프로토타입 언어의 특징을 어떻게 구현할까? 이전에 접했던 자바스크립트에서의 프로토타입에 대한 글이 어려웠다면, 이 글을 통해서 조금이나마 쉽게 이해할 수 있기를 기대한다.
자바스크립트는 프로토타입 기반 언어다. 프로토타입 기반 언어란 무엇일까? 위키피디아에는 다음과 같이 정의되어 있다.
프로토타입 기반 언어는 클래스 기반 언어에서 상속을 사용하는 것과는 다르게, 객체를 원형(프로토타입)으로 하는 복제 과정을 통해 객체의 동작 방식을 재사용 할 수 있게 한다.
프로토타입 기반 언어는 원형 객체를 복제하여 새로운 객체를 하는 생성하는 언어를 말한다. 그런데 자바스크립트도 복제를 하는가? 자바스크립트는 약간 다르다. 복제가 아닌 프로토타입 링크를 통해 원형을 참조한다.
그러면 프로토타입 링크란 무엇인가?
프로토타입 링크를 설명하려면, 우선 자바스크립트의 객체에 대해 먼저 알아야 한다.
자바스크립트에서 단순 원시 타입(simple primitive)인 문자열, 숫자, 불리언, null, undefined를 제외한 모든 타입은 객체다.
그렇다. 자바스크립트에서는 배열도 객체고 함수도 객체다.
자바스크립트에서 객체는 원형 객체로 부터 생성되며, 생성된 객체는 원형에 대한 프로토타입 링크(__proto__
)를 갖게 된다.
__proto__
는 원형에 대한 참조 정보를 갖고 있는 객체의 내부 속성으로, ES6 부터는 표준으로 제정되었다.
원형 또한 객체이기 때문에 원형은 또 다른 원형을 참조하게 되고, 다음 그림과 같이 연속된 프로토타입 링크를 통해 자바스크립트 객체의 최종 원형인 Object.prototype
까지 연결된다.
Object.prototype
객체에는toString
,hasOwnProperty
함수 등과 같이 자바스크립트 객체에서 흔히 사용하던 속성들이 정의되어 있고, 그로 인해 모든 객체에서 해당 속성들을 사용할 수 있다.
그림과 같이 객체 간에 형성되어있는 일련의 링크를 프로토타입 체인이라고 부른다.
프로토타입 체인에 대해 조금 더 알아볼 필요가 있을 것 같다.
우선 foo라는 객체에 다음과 같이 속성을 정의한다.
당연한 얘기겠지만 foo.a
는 foo.b
는 2를 반환한다.
그렇다면 foo
객체에 정의되지 않은 foo.c
를 호출하게 되면 어떤 값을 반환할까? undefined를 반환할까?
foo
객체의 프로토타입 체인을 보기 전까지 알 수 없다.
이것이 foo
객체의 프로토타입 체인이다. 이 체인에서 foo.c
는 무엇을 반환할까?
foo
객체의 속성에 접근하게 되면 프로토타입 체인은 호출한 foo
객체의 속성부터 Object.prototype
까지까지 프로토타입 링크를 따라 차례차례 탐색하기 시작한다.
위 그림에서는 '원형 객체2'에 c
가 정의되어 있기 때문에 foo.c
는 undefined
가 아닌 7을 반환한다.
만약, 체인 상의 어떤 객체에도 존재하지 않는 foo.d
에 접근하게 되면, 어떤 값을 반환할까?
프로토타입 체인의 최종 원형인 Object.prototype
까지 탐색한 후 값이 없음을 확인하고 undefined
를 반환한다.
자바스크립트에서는 객체 속성에 접근하게 되면 해당 객체의 속성들만 탐색한 후 결과를 반환하는 것이 아니라, 최종 원형인 Object.prototype
까지 탐색한 후 결과를 반환한다는 것을 기억하자.
그러면 이제 프로토타입 체인을 코드로 직접 구현해보자.
Object.create
함수로 자바스크립트의 프로토타입 체인 구현하기ES5 이전에는 프로토타입 체인을 구현하려면 무조건 생성자 함수와 new
연산자를 사용해야 했다.
new
연산자 사용은 ES5 이전의 유일한 객체 생성 방법이며 리터럴(literal) 방식도 내부적으로는new
연산자를 사용한다.
그러나 클래스 기반 언어를 따라 한 new
연산자는 프로토타입 체인을 복잡하게 만들어, 사용자로 하여금 프로토타입 체인에 대한 구현을 어렵게 했다.
다행스럽게도 ES5부터는 Object.create
라는 프로토타입 언어의 특징을 잘 살려 객체를 생성할 수 있는 새로운 방법을 제공한다.
여기서는 Object.create
를 사용하여 프로토타입 체인에 대해 보다 쉽게 구현하는 방법에 대해 설명하겠다.
Object.create 함수는 ES5부터 지원하는 함수이며, 인자로 전달된 객체를 원형으로 하는 새로운 객체를 생성하는 기능을 한다.
먼저 init
함수와 identify
함수를 정의한 foo
객체를 생성한다.
var foo = {
init: function(who) {
this.me = who;
},
identify: function() {
return "I am " + this.me;
}
};
foo
객체는 최종 원형 객체인 Object.prototype
를 참조하고 있다.
다음으로 foo
객체를 원형으로 하는 bar
객체를 생성해보자.
var bar = Object.create(foo);
bar.speak = function() {
return "Hello," + this.identify();
};
bar
객체는 foo
객체를 원형으로 생성되었기 때문에 프로토타입 링크(__proto__
)를 통해 foo
를 참조하고 foo
에서 정의한 init
과 identify
함수를 모두 사용할 수 있다.
bar
객체에 정의한 speak
함수를 보면 foo
객체에서 정의한 identify
함수를 사용하는 것을 볼 수 있다.
이제 마지막으로 bar
를 원형으로 하는 객체를 생성해보자.
var baz = Object.create(bar);
baz.init("baz");
baz.speak(); // Hello, I am baz
Object.create
함수를 이용하여 baz
객체를 생성했다.
baz
의 원형 객체는 bar
이며 bar
의 원형 객체는 foo
이기 때문에, baz
에서는 foo
의 init
과 bar
의 speak
모두 사용이 가능하다.
Object.create
함수를 사용하여 자바스크립트의 프로토타입 체인을 구현해 보았다. 위에서 설명한 개념을 아주 쉽게 구현할 수 있었다.
ES5를 지원하지 않는 브라우저에서 Object.create
를 사용하려면 MDN에서 제공하는 폴리필(polyfill)을 추가하면 된다.
폴리필(polyfill)이란 브라우저에서 지원하지 않는 api를 보충해 주는 코드를 말한다.
__proto__
)를 통해 원형을 참조한다.