본문 바로가기

자바스크립트

[Javascript] - 프로토타입 상속

 

*프로토 타입 상속

 

- 사람에 관한 프로퍼티와 메서드를 가진 user라는 객체가 있는데 user와 상당히 유사하지만 약간의 차이가 있는 admin과 guest 객체를 만들어야 한다고 가정해보면 user의 메서드를 복사하거나 구현하지 않고 user에 기능을 얹어 admin과 guest객체를 만들고 싶을 때 자바스크립트 고유 기능인 프로토타입 상속을 이용할 수 있다 

 

 

[Prototype]

 

- 자바스크립트의 객체는 명세서에 명명한 [Prototype] 이라는 숨겨진 프로퍼티를 갖는다 이 숨김 프로퍼티의 값은 Null이거나 객체에 대한 참조가 되는데 다른 객체를 참조하는 경우 참조 대상은 프로토타입이라고 부른다.

 

 

 

- 프로토타입의 동작 방식은 신비스러운 면이 있다 object에서 프로퍼트를 읽으려고 하는데 해당 프로퍼티가 없으면 자바스크립트는 프로토타입에서 프로퍼티를 찾는다.

 

- 프로그래밍에선 이런 동작 방식은 프로토타입 상속이라고 부른다 언어차원에서 지원하는 편리한 기능이나 개발 테크닉 중 프로토타입 상속에 기반해 만들어진 것들이 많다.

 

- [[Prototype]]프로퍼티는 내부 프로퍼티면서 숨김 프로퍼티지만 다양한 방법을 사용해 개발자가 값을 설정할 수 있다 예로 _proto__ 를 사용하면 값을 설정할 수 있다.

 

__proto__

__proto__는 [[Prototype]]과 다르다  __proto__는 [[Prototype]]의 getter(획득자)이자 setter(설정자)이다

하위 호환성 때문에 여전히 __proto__ 를 사용 할 수 있지만 비교적 근래엔 작성된 스크립트에선 __proto__대신 함수

Object.getPrototypeOf나 Object.setPrototypeOf를 써서 프로토타입을 get 하거나 set 한다. _proto_는 브라우저환경에서만 지원하도록 자바스크립트 명세서에 규정하였는데 실상은 서버 사이드를 포함한 모든 호스트 환경에서 __proto__를 사용한다.

 

 

- 객체 rabbit 에서 프로퍼티를 얻고 싶은데 해당 프로퍼티가 없다면 자바스크립트는 자동으로 animal이라는 객체에ㅐ서 프로퍼티를 얻는다.

 

위 예제에서 rabbit에는 eat이라는 프로퍼티가 없다 이때 자바스크립트 [Prototype]] 이 참조하고 있는 객체인 animal에서 eats를 얻어낸다.

 

이제 rabbit의 프로토타입은 animal이다 혹은 rabbit은 animal을 상속받는다 라고 말할 수 있다.

프로토타입을 설정해 준 덕분에 rabbit에서도 animal에 구현된 유용한 프로퍼티와 메서드를 사용할 수 있게 됐다 이렇게 프로토타입을 상속받은 프로퍼티를 상속 프로퍼티라고 한다.

 

 

위 예제에서 animal에다 walk메서드를 추가했다.

 

 

이제 위 그림같이 프로토타입(animal) 에서 walk를 상속받았기 때문에 rabbit에서도 walk를 호출 할 수 있다.

 

프로토타입 체인

 

 

 

 - 프로토타입 체이닝엔 두가지 제약사항이 있다.

 

1 순환 참조는 허용되지 않는다 _proto_ 를 이용해 닫힌 형태로 다른 객체를 참조하면 에러가 발생한다.

 

2 _proto_의 값은 객체나 null만 가능하다 다른 자료형은 무시된다.

 

쓸 때는 프로토타입을 사용하지 않는다.

 

프로토타입은 프로퍼티를 읽을 때만 사용한다 즉 프로퍼티를 추가 수정하거나 지운느 연산은 객체에 직접 해야 한다

 

 

- rabit.walk() 를 호출하면 프로토타입에 메서드가 실행되지 않고 객체 rabbit에 추가한 메서드가 실행된다.

 

 

 

- 접근자 프로퍼티는 setter 함수를 통해서 프로퍼티에 값을 할당하므로 이 규칙이 적용되지 않는다 접근자 프로퍼티에 값을 할당하는 것은 함수를 호출하는 것과 같기 때문이다.

 

 

- (*) 로 표시한 줄에서 admin, fullName 프로토타입(user)에 있는 getter 함수 를 호출하고 (**)로 표시한 줄의 할당 연산은 프로토타입에 있는 setter 함수 를 호출한다.

 

this가 나타내는 것

 

결론부터 말하자면 this는 프로토타입에 영향을 받지 않는다.

메서드를 객체에서 호출했든 프로토타입에서 호출했든 상관없이 this는 언제나 .앞에 있는 객체가 된다.

만약 admin.fullName=으로 setter함수를 호출할 때 this는 user가 아닌 admin이다.

객체를 하나를 만들고 여기에 메서드를 많이 구현해 놓은 다음 여러 객체에서 이 커다란 객체를 상속받게 하는 경우가 많기 때문에 이런 특징을 잘 알아두면 좋다.

상속받은 메서드를 사용하더라도 객체는 프로토타입이 아닌 자신의 상태를 수정한다.

 

 

 

위 예제를 보면 rabbit 말고도 bird, snake등이 animal을 상속받는다고 가정하면 이 객체들도 rabbit 처럼 animal에 구현된 메서드를 사용할 수 있다 이때 상속받은 메서드와 this는 animal이 아닌 실제 메서드가 호출되는 시점의 점(.)앞에 있는 객체가 된다 

 

따라서 this에 데이터를 쓰면 animal이 아닌 해당 객체의 상태가 변화한다.  즉 메서드는 공유되지만 객체의 상태는 공유되지 않는다가 결론이다.

 

프로토타입과 for...in 반복문

 

for...in은 상속 프로퍼티도 순회대상에 포함시킨다.

 

 

- obj.hasOwnProperty(key)를 이용하면 상속 프로퍼티를 순회 대상에서 제외할 수 있다 이 내장 메서드는 key에 대응하는 프로퍼티가 상속 프로퍼티가 아니고 obj에 직접 구현되어 있는 프로퍼티일때 true를 반환한다.

 

 

- obj.hasOwnProperty(key)를 응용하면 위 예제처럼 상속 프로퍼티를 걸러낼 수 있고 상속 프로퍼티만을 대상으로 무언가를 할 수도 있다.

 

 

위 예제의 상속관계를 그림으로 나타내면 다음과 같다.

 

rabbit은 animal을 animal은 Object.prototype을 Object.prototype은 null을 상속받고 있다.

animal이 Object.prototype을 상속받는 이유는 객체 리터럴 방식으로 선언하였기 때문이다.

for...in안에서 사용한 hasOwnProperty가 Object.prototype.hasOwnProperty에서 왔다는 것을 확인할 수 있다.

 

덤으로 hasOwnProperty는 열거 가능한 프로퍼티가 아니라 console.log로 출력되지는 않았다 Object.prototype에 있는 모든 메서드와 enumerable 플래그는 false인데 for...in은 오직 열거 가능한 프로퍼티만을 순회대상에 포함하기 때문이다.