2015년 8월 14일 금요일

자바스크립트 오브젝트 모델 및 상속구조


자바스크립트의 오브젝트 모델 및 상속구조에 대해서 자세하게 살펴본다. 학습테스트는 자바스크립트 테스트 프레임워크인 자스민을 이용해서 작성되었다. 자스민에 대한 사용법은 이 전에 작성했던 "자스민 사용법" 글을 참조하자.

클래스 기반의 객체모델과  프로토타입 기반의 객체 모델

자바와 같은 클래스 기반의 객체 모델을 사용하는 언어에서는 두 가지 분명하게 구분되는 개념이 있다. 바로 클래스와 인스턴스이다. 클래스는 특정 집합에 속하는 객체들의 특성들을 멤버변수와 메서드로 추상화한다.예를 들어, Talent 클래스는 "재능이 있는 사람들" 의 일반적인 특성을 추상화 한다. 반면에 인스턴스는 특정 재능이 있는 사람 한 명을 나타낸다. 예를 들어 SteaveJobs는 Talente  클래스가 실체화된 인스턴스로 Talent 클래스의 모든 속성들을 가지고 있다.   

자바스크립트와 같은  프로토타입 기반의 객체 모델을 사용하는 언어에서는 클래스와 인스턴스의 명확한 구분이 없고, 단순히 오브젝트이다. 이러한 언어에서는 프로토타입이 되는 오브젝트가 존재한다. 그리고 프로토타입 오브젝트는 다른 오브젝트의 템플릿이 된다. 오브젝트가 생성이 될 때이던지, 실행시간이던지 어떤 오브젝트도 다른 오브젝트의 프로토타입이 될 수 있다. 프로토타입을 통해서 생성된 오브젝트는 프로토타입의 속성들을 공유한다.

자바스크립트에서 오브젝트를 만들 때, 자바에서과 같이 별도의 클래스를 정의할 필요없다. 대신 생성자 함수을 통해서 특정 속성들을 가지는 오브젝트를 정의할 수 있다. 어떤 자바스크립트 함수도 생성자 함수가 될 수 있으며,"new" 키워드와 생성자 함수를 이용해서 새로운 오브젝트를 생성할 수 있다.

Class-based
Prototype-based
클래스와 인스턴스가 구분된다.
모든 오브젝트는 다른 오브젝트로부터 상속될 수 있다.
명시적인 클래스의 정의가 필요하다. 클래스의 인스턴스화는 생성자를 통해서 가능하다.
생성자 함수를 통해서 오브젝트의 정의 및 생성이 가능하다.
new 연산자를 통해서 한 개의 객체를 생성한다.
동일하다.
클래스 정의를 통해서 서브클래스 및 클래스 상속구조를 정의한다.
생성자 함수와 함께 prototype 오브젝트를 할당함으로 상속구조를 정의한다.
실행시간에 동적으로 속성을 추가하는 것이 불가능하다.
생성자 함수나 prototype 은 단순히 최초 속성들의 집합만을 정의한다. 동적으로 속성을 추가하거나 제거하는 것이 가능하다.

이 포스트에서는 아래와 같은 상속구조를 가지는 예제를 이용할 것이다. 클래스 다이어그램에서 관해서는 UML 클래스 다이어그램 을 참조하자.



1. 상속구조
먼저 Talent 오브젝트와 이 오브젝트를 상속한 Painter 오브젝트를 살펴보자. [라인 10]을 살펴보면 Painter 오브젝트에서 Talent.call(this)  메서드를 호출하고 있다. call 함수에 대해서는 여기를 살펴보자. call 함수에 "this"  인자와 필요에 따라 추가적인 인자를 넘김으로서 상속받고자 하는 오브젝트의 생성자 함수를(여기서는 Talent ) 호출할 수 있다. 그리고 나서 [라인 13]에서 Painter.prototype 속성에 Talent.prototype 를 생성해서 할당해 주고 있다. prototype 은 자바스크립트에서 사용하는 특별한 속성으로서 모든 함수는 이 속성을 가지고 있다. 참고로 함수는 일반적으로 function funcName() {...} 으로 선언된 것을 의미하고, 메서드는 특정 오브젝트 멤버로서 존재하는 것을 의미한다.

자바스크립트의 내부적인 객체 생성과정을 살펴보자.

var psy = new Singer;

자바스크립트는 new 오브젝트가 있는 경우,  generic object을 생성해서 Singer 오브젝트의 생성자 함수의 this  키워드의 value 로 넘겨준다. 그리고 나서 생성자 함수에서 명시적으로 "majorTalent”속성을 할당한다. 그 다음으로 묵시적으로 내부적인 __proto__ 속성을 Singer 생성자 함수의 prototype 으로 할당한다. (__proto__ 속성은 특정 속성을 리턴할 때, 특정 속성의 값을 리턴하는  prototype 을 결정한다.)

자바스크립트에서 특정 속성을 읽을 때, 자바스크립트는 먼저 해당 속성이 오브젝트에 존재하는지 확인한다. 만약 있으면 해당 값을 리턴한다. 만약 없다면, 자바스크립트는 __proto__  속성을 이용해서  프로토타입 체인을 확인한다. 프로토타입 체인에 있는 오브젝트가 해당 속성의 값을 가지고 있으면, 그 값을 리턴한다. 만약 오브젝트가 프로토타입 체인에 존재하지 않으면, 자바스크립트는 해당 속성이 존재하지 않는다고 말한다.


2. 좀 더 유연한 객체 생성방법 - 생성자 함수에 매개변수 전달
생성자 함수가 인자를 받을 수 있게 하여 사용한다.


3. 좀 더 유연한 객체 생성방법 - 기본값 설정
[라인 4 ~ 6]에서와 같이 "||" 연산자를 이용해서 속성에 기본값을 설정할 수 있다. 값이 있는 경우에는 인자로 넘어온 값을 그렇지 않은 경우에는  empty 값을 설정한다. [라인 11]에서는 base 속성에 Talent 오브젝트의 생성자 함수를 설정하고 있다. 사실 base 속성은 특별한 속성이 아니다. 어떤 이름도 가능하다. 그리고 나서 [라인 12] 에서 생성자 함수를 호출하고 있다.

4. 왜 "prototype" 속성을 명시적으로 할당하는 것을 선언해주어야 하는가?
TalentedForMusic 과 TalentedForPaint 생성자 함수를 잘 살펴보자. TalentedForMusic 은 부모 생성자 함수의 prototype 속성을 할당해 주지 않았다. [라인 43 - 46] 을 살펴보자. [라인 43]에서처럼 Talent.prototype 에 추가적인 속성을 정의하는 경우, 부모 생성자 함수의 prototype 속성을 할당하지 않았던 TalentedForMusic 의 인스턴스인 hummingMan 오브젝트에는 이 속성이 추가되지 않는다.

5. "객체 생성자 함수에서 속성" vs "object.prorotype.property = value"
Talent의 "name" 속성과 "grade" 속성을 유심히 살펴보자. "name" 속성은 생성자 함수 내부에서 선언을 했고, "grade" 속성은 Talent.prototype.grade = "none" 과 같이 prototype 의 속성으로 선언을 했다. 이것은 주요한 차이가 있다. [라인 32 - 43] 에서와 같이 name 속성에 새로운 값을 할당하더라도, 그 인스턴스의 값은 변하지 않는다. 하지만, prototype 의 속성으로 선언되는 경우 [라인 46 - 48] 에서 볼수 있듯이, 새로운 값을 할당하는 경우 그 오브젝트의 인스턴스들의 값들이 모두 변하는 것을 알 수 있다.

6. instanceof, __proto__
[라인 38] 에서과 같이 instanceof 을 통해서 어떤 오브젝트의 인스턴스인지 알 수 있다.
자바스크립의 Object을 제외한 모든 오브젝트는 __proto__ 라는 특별한 속성을 가지고 있다. (자바스크립의 모든 함수는 prototype 이라는 특별한 속성을 가지고 있다.) 이 속성은 오브젝트가 생성될 때 생성자 함수의 prototype 이 할당된다. [라인 39 - 41]에서 볼 수 있듯이 오브젝트의 __proto__ 속성과 생성자 함수의 prototype 속성이 동일함을 알 수 있다.

7. 오브젝트 생성자 함수에서의  글로벌 변수 사용
오브젝트의 생성자 함수에서 글로벌 변수를 사용을 할 때는 주의를 하여야 한다. [라인 35]에서 count가 1일 것을 기대했을 수도 있지만 사실 결과는 3이다. [라인 32], [라인 29], [라인 17]에서 호출되기 때문이다. 즉, prototype 을 할당할 때도 호출된다. 


8. 자바스크립트는 다중 상속을 지원하지 않는다.
TalentForMusic 생성자 함수에서 Talent 와 Personality 두 오브젝트를 생성하는 것처럼 보인다. 실제로  Personality 의 속성을 TalentForMusic 에서 사용할 수 있다. 하지만, [라인 35]에서 보는 것처럼 Personality에 새로운 속성을 추가하는 경우, TalentForMusic 에는 추가되지 않는 것을 알 수 있다.


9. 참조
- MDN - https://developer.mozilla.org/en/docs/Web/JavaScript
- Jasmine - http://jasmine.github.io/2.3/introduction.html

댓글 없음:

댓글 쓰기