'2016/06/17'에 해당되는 글 1건

  1. 2016.06.17 [자바스크립트] 프로토타입에 대하여
반응형

1. 프로토타입이란?

 C++이나 자바의 경우 클래스 기반의 객체 상속을 지원한다. 자바스크립트는 프로토타입 기반의 객체 상속을 지원하다. 모든 자바스크림트의 객체는 자신의 부모인 프로토타입을 가르키는 참조 링크 형태의 프로퍼티가 있다. 이를 암목적 프로토타입 링크(implicit prototype link)라고 하며 [[Prototype]] 링크이다. 구글 크롬 브라우저와 node.js에서는 __proto__ 로 접근할 수 있다.


1.1 객체 리터럴 방식으로 생성된 객체

 객체는 자기의 프로퍼티 뿐만 아니라 부모 객체의 프로토타입 객체의 프로퍼티도 접근 가능하다. 정확히 말하자면 해당 객체에 프로퍼티가 없으면 [[prototype]] 에 연결된 객체에서 프로퍼티를 찾는다. 이를 프로토타입 체이닝이라고 한다. 다음의 예제를 보면 객체 리터럴 방식으로 생성한 객체는 Object.property 객체를 [[prototype]] (예제에서는 __proto__) 링크를 가진다. testObj는 hasOwnProperty 메소드를 가지고 있지 않지만, Object.property 객체는 hasOwnProperty 메소드가 있어서 프로토타입 체이닝을 통해서 hasOwnProperty 메소드를 호출 가능하다. 


1.2 생성자 함수를 통한 프로토타입 체이닝

 생성자 함수 방식으로 객체를 생성하는 경우에는 객체 리터럴 방식과는 다른 형태의 프로토타입 체이닝이 이루어진다. 하지만 다음의 원칙을 가지고 있다.


 "자바스크립트에서 모든 객체는 자신을 생성한 생성자 함수의 prototype 프로퍼티가 가르키는 객체를 자신의 프로토타입 객체(부모객체)로 취급한다."


 함수 객체가 만들어 질 때, 함수를 생성하는 Function 생성자는 this.prototype={constructor:this} 와 같은 코드를 실행하는 것이다. 이 객체는 상속할 것들이 저장되는 장소이다.

 자바스크립크에서 주의해야 할 것은 함수의 prototype 프로퍼티와 __proto__ 프로퍼티를 잘 구별해야 한다는 것이다. 앞선 포스팅에서 함수를 생성하면 prototype 프로퍼티를 가지고 있고 이 prototype은 해당 함수로 생성한 객체의 부모 역활을 하는 프로토타입 객체이다. 생성자 함수 방식으로 객체를 생성하면 객체는 함수의 prototype이 가르키는 객체를 __proto__ 로 가르키게 되는 것이다. 

 다음 그림은 함수 리터럴로 함수를 생성했을때의 prototype, __prototype__ 링크의 관계이다.

 testFunc 함수를 생성하면 testFunc.prototype 객체가 생성되는데, 이 객체는 함수를 가르키는 constructor 프로퍼티를 가지고 있다. testFunc.prototype의 [[prototype]] 다시 말해 __proto__는 Object.prototype을 가르킨다. Object.prototype은 constructor로 Object를 가르키고, Object의 [[property]는 Function.prototype을 가르킨다. Function.prototype의 constructor는 Function을 가르키고 Function의 prototype과 __proto__는 Function.prototype을, Function.prototype의 [[prototype]]은 Object.prototype을 가르킨다.

 보는 것처럼 Object.prototype은 [[prototype]] 프로퍼티를 가지지 않는다. Object.prototype은 프로토타입 체이닝의 종점이다. 

 testFunc 함수를 new로 호출하여 객체를 생성하면 다음의 그림처럼 새로운 객체를 생성하고, 이 객체의 [[prototype]] 링크를 해당 함수의 prototype 객체로 가르키게 한다.


 리터럴로 생성된 객체, new 생성자로 생성된 객체, 함수 리터럴로 생성된 함수 모두 최종적으로는 Object.prototype 의 프로퍼티에 접근한다. Object.prototype 에 프로퍼티나 메소드를 추가하면 모든 객체, 함수에서 접근 가능한 프로퍼티나 메소드가 된다. 이는 숫자, 문자열, 배열 등에서 사용되는 표준 메소드들도 확장할 수 있음을 의미한다. (Number.prototype, String.prototype, Array.prototype)



1.3 프로토타입과 this 바인딩

 prototype 객체는 메서드를 가질 수 있다. prototype 의 메소드에서 this 를 사용하게 되면 this는 해당 메소드를 호출한 객체를 가르킨다. 다음의 예제를 보자

 testFunc 함수에는 생성되는 객체에 getName이라는 함수를 생성하게 한다. 그리고 testFunc.prototype에 getName이라는 메소드를 추가하였다. 이 함수로 testObj를 생성하고 getName을 호출하게 되면 상기 1) 처럼 객체 메소드를 호출하게 되어 'hello'가 출력된다. 그리고 객체의 getName 메소드를 삭제하고 다시 getName을 호출하게 되면 객체에는 getName 프로퍼티가 존재하지 않으므로 프로토타입 체인상에서 메소드를 찾게 된다. 그래서 2)처럼 'prototype:hello'가 출력된다. 여기에서 this는 해당 메소드의 함수를 호출하는 객체에 연결된다. 

 부모 객체를 가르키는 [[prototype]]은 다른 객체로 언제든지 변경이 가능하다. 상기 예제 코드에 다음을 추가하면 동적으로 바뀐 [[prototype]]의 getName 메소드를 호출할 수 있다.


 


반응형
Posted by alias
,