1. 기술/웹, 자바스크립트

JavaScript - 객체와 Prototype

swsong 2019. 10. 21. 00:24
페이스북 '프론트엔드개발그룹' 에서 피드백받은 내용을 추가하였습니다.(표시 /*)

자바스크립트는 프로토타입(Prototype) 기반 언어다. 자바, 파이썬처럼 객체지향이라는 점에서 맥락을 같이하지만 클래스 기반이 아닌 프로토타입 기반이기 때문에 '상속' 개념이 없다.

/*

자바스크립트에도 상속이 있다고 합니다.

class-based oop에서와 다른 방식의, 다른 형태의 상속으로,

ES 명세에 있는 다음 구절이 이를 잘 설명해주고 있습니다.

"In a class-based object-oriented language, in general, state is carried by instances, methods are carried by classes, and inheritance is only of structure and behaviour. In ECMAScript, the state and methods are carried by objects, while structure, behaviour, and state are all inherited."

*/

 

대신 자바스크립트는 프로토타입의 복사(Cloning)와 확장을 통해 새로운 객체를 만들어내게 된다. 이러한 차이점 때문에 자바스크립트에서 제대로 된 객체지향적 접근을 하기 위해서는 프로토타입에 대해 반드시 이해해야 한다. 프로토타입을 왜 사용해야 하는지 그리고 어떻게 동작하는지에 대해 간단한 메커니즘을 살펴보자.

프로토타입은 '객체 원형'을 말한다. 그리고 모든 객체는 최상위 객체인 객체 원형을 참조한다.

/*

 Object.prototype을 참조하지 않는 객체들도 존재한다고 합니다.
예로 Object.create(null); 로 생성되는 객체가 있습니다.

*/

 

객체가 내용을 담고 있는 형태는 복사가 아닌 참조 형태로 그 원형의 위치를 가리키는 것과 같은 맥락이다. 물론 내부적으로 매우 복잡하게 연결된 구조를 가지기 때문에 Prototype Object와 Prototype Link에 대해 이해할 필요는 있지만 작성자가 부족하여 본문에서 다루지 않겠다.

 

본론으로 돌아와서, 객체의 참조 방식은 메모리를 효율적으로 사용하는 것과 매우 밀접하게 관련이 있다. 프로토타입의 개념이 없다고 가정하면 코드의 양이 길어졌을 때 그리고 객체가 한눈에 보기 어려울 정도로 많아졌을 때 불필요한 메모리가 매우 많이 낭비된다. 왜 그럴까?

대부분의 객체는 유사한 기능을 많이 가지고 있다. 가장 쉬운 예로 배열에는 항상 length가 있다. 사실 length는 알고리즘 문제에서도 매우 많이 접하는데 배열에 접근하고 사용할 때에 자주 등장한다. 

코드는 재사용성이 매우 중요하다. 만약 이렇게 length와 같이 매우 자주 쓰이고 대부분의 객체에서 활용할 수 있는 속성을 모든 객체가 따로따로 원본 형태 그대로 가지고 있기보다 특정 객체에 필수적인 기능들을 만들어 놓고 모든 객체들이 이를 이용할 수 있도록 한다면 메모리를 절약할 수 있다.

/*

(맥락상 수정 없이 인용했습니다)

배열의 인스턴스의 length는 배열의 프로토타입 객체에 들어있는 프로퍼티가 아닙니다.(실제로 있긴 하나, 대부분의 경우에서 코딩할 때 참조하는 녀석이 얘는 아닙니다.). 각 인스턴스의 개별적인 값을 요구하기 때문에 프로토타입에 넣는 게 메모리 절약 면에서 큰 효과를 발휘하지도 않구요.

*/

여기서 말한 특정 객체는 Object라는  최상위 객체(전역 객체)다. Object는 객체가 필요한 주요 속성들을 모두 가지고 있고

/*

이 부분은

'대부분의 객체가 필요로 하는 기본적인 속성들'

으로 이해해주시면 좋겠습니다.

*/

 

모든 객체는 Object에서 파생된다. 이해를 돕기 위해 빈 객체를 하나 만들어보았다.

 

무슨 내용인지는 몰라도 분명 빈 객체를 만들었는데 객체 안에 __proto__라는 속성이 들어가 있는 것을 볼 수 있다. __proto__가 바로 우리가 만든 객체를 객체 원형 Object()와 연결해주는 숨겨진 속성이다.

/*

정확히는

Object Prototype을 참조합니다.

사실 이 부분은 하단에

부가 설명을 해두었지만

오해의 소지가 있는 부분이라

수정합니다.

*/

__proto__의 생성자(constructor)를 보면 Object()가 있다. 그리고 Object()가 함수(f)라는 것도 알 수 있다. __proto__ 는 단순히 객체 원형과 연결해주는 속성이 아니라 상위 객체를 가리키는데, 다음과 같이 상위 객체를 지정해주면 __proto__가 총 2번 등장해서 가장 깊숙하게는(최종적으로) 객체 원형까지 연결되는 모습을 볼 수 있다.

/*

모든 객체의 프로토타입 체인 최상위 값은 객체 원형이 아닌

'null'이라고 합니다.

*/

 

마치 스코프를 보는 것 같다. 프로토타입 역시 스코프처럼 상하로 연결되는 구조를  가지고 있기 때문에 비슷하게 이해해주면 좋다. 이번에는 생성자 함수를 통해 __proto__를 살펴보자.

new를 통해 생성자 함수를 사용했기 때문에 생성자(constructor)는 자기 자신이다. 그리고 prototype이라는 새로운 속성이 등장했는데, 이는 생성자 함수만 가질 수 있는 객체의 '원본'을 뜻한다.

prototype에서 똑같은 속성들이 반복되는데, 생성자 함수로 만들어졌기 때문에 자기 자신을 원형으로 가지고 있는 것이다.

마지막으로, __proto__는 상위 객체, 최종적으로는 객체 원형을 가리킨다고 했지만 정확히는 상위 객체의 'prototype'를 가리키고 있다.  그리고 자바스크립트가 ES6부터 class라는 문법을 제공하지만 파이썬과 같이 다른 언어에서 사용하는 class와는 분명한 차이가 있다.