본문 바로가기

Frontend/Javascript

[javascript] 프로토타입이 대체 뭔데

728x90
Javascript는 흔히 프로토타입 기반 언어(prototype-based language)라 불린다.
그래서 프로토타입을 이해하면 Javascript를 더 잘 이해할 수 있다던데...

JS는 다중 패러다임 언어

프로토타입은 객체 지향과 관련이 크기 때문에 JS가 다중 패러다임 언어라 객체 지향적으로 코드를 작성할 수 있다는 사실을 넘겨 짚고 가자!

그렇다면 객체 지향이 뭐야?

객체를 여러 개 만들어서 프로그램을 더 쉽게 짜는 것을 지향하는 프로그래밍 방식이다.

예를 들어, 코드로 차를 만드는 과정을 나타낸다고 했을 때 왼쪽처럼 차와 관련 있는 속성이나 기능을 순서대로 만들 수도 있지만, 오른쪽처럼 하나의 덩어리(객체)로 묶을 수도 있다. 객체로 묶으면 코드를 더 깔끔하게 정리할 수 있다.

 

차를 만드는 예시로 설명한 객체지향

Class

객체 지향하면 가장 먼저 떠오르는 건 보통 Class 일 것이다! class에서는 객체 지향 프로그래밍에서 객체를 설계하기 위해 만들어두는 일종의 틀(template)을 의미한다. 

예를 들어 차를 만들 때 차의 색상을 지정하고, 트렁크의 크기를 지정해주어야 하며, 악셀을 밟을 수 있는 기능, 와이퍼가 작동할 수 있는 기능을 만들어야 한다면, 이 모든 기능을 포함하고 있는 차를 자동으로 만들어줄 수 있는 마법 기계가 class 인 것이다. 아직 만들어진 차는 없지만 기계를 작동만 한다면 차가 뚝딱뚝딱 생기는 것이다.

 

class

 

그렇다면 자바스크립트는 객체 지향을 어떻게 지원할까

자바스크립트는 프로토타입 기반 언어로, 프로토타입을 이용해 객체 지향을 지원한다. 프로토타입의 정의는 무엇일까?

프로토타입(prototype)은 원래의 형태 또는 전형적인 예, 기초 또는 표준이다. (위키피디아)

 

프로토타입의 정의에서 알 수 있다시피 프로토타입 기반 프로그래밍은 객체의 원래의 형태를 복제하면서 객체 지향을 지원하는 것이다.

차를 만드는 예시를 살펴보면, class 기반 언어는 일종의 틀을 만들어두고 틀을 이용해 실제 자동차를 만든다면, prototype 기반 언어는 실제 차를 만들어두고 이 차를 고대로 가져와서 개조(?)해 사용한다고 생각하면 된다.

 

class vs prototype

 

복제의 문제: 메모리 낭비와 부모가 누군지 몰라요

프로토타입 기반 언어는 객체의 원래 형태를 복제하면서 객체 지향을 지원한다고 했는데...

위의 차를 만드는 과정을 예시로 들었을 때 만약 악셀 밟기() 함수의 코드가 엄청 길어서 메모리를 엄청 잡아먹는다면?

 

제하는 모든 객체들이 같은 코드를 가지면서 메모리 낭비가 발생하게 될 것이다.

또한, 복제된 객체의 원본 객체가 누구인지 알기 어렵다는 문제가 있다.

 

이러한 문제를 해결하기 위해 위에서 언급했던 객체의 원래의 형태를 복제한다는 내용은 정확히 설명하면 객체 자체를 동일하게 복제하는 것이 아니라 class의 설계서처럼 객체의 원형을 따로 만들어 두고, 복제할 객체들 또한 원형을 참조하는 형식으로 만들어진다. 복제하는 객체들은 원본 객체의 원형, 즉 프로토타입을 참조하는 것이다.

 

 

프로토타입 만들기

JavaScript에서는 함수가 일급 객체이면서 동시에 프로토타입 기반 상속을 지원하는 특수한 유형의 객체이기 때문에 함수를 이용해 객체를 만들어 본다.

var Car = function(color, trunkSize) {
	this.color = color; // 차 색상
	this.trunkSize = trunkSize; // 트렁크 크기
};

Car.prototype.goForward = function() { // 악셀 밟기
	console.log(this.color, "색상 차로 달려보자~");
}; 

 

아래 이미지를 보면 만든 Car 객체 내부에 prototype이 생긴 것을 알 수 있다! 바로 원본 객체의 원형인 prototype! 

prototype에는 위 코드에서 추가한 goForward 함수도 추가되어 있지만, constructor와 [[prototype]] 속성이 자동으로 생긴다.

Car 객체 구조

 

우리가 객체를 생성하면 객체의 원형인 prototype에 해당하는 object(Prototype Object)가 같이 생성된다. 우리가 생성한 객체 내부에는 prototype 속성이 생기고 이 속성은 prototype object를 참고한다. 그리고 prototype object에는 자동으로 constructor와 __proto__(= [[prototype]]) 속성이 생긴다. constructor는 생성한 원래 객체를 참조하며 __proto__는 객체가 생성될 때 조상이었던 함수의 prototype object를 가리킨다. 

 

 

그렇다면 Car를 이용해서 새로운 객체를 만든다면?

var yellowCar = new Car("yellow", 100);

 

 

새로운 객체에는 __proto__(= [[prototype]]) 속성이 자동으로 담긴 것을 알 수 있다. 새로운 객체가 생기면 모든 객체는 원본 객체의 prototype Object를 참조한다.

 

아까 설명했던 이미지처럼...!

 

 

[[Prototype]] 접근법

그럼 새로운 객체에서 원본 객체의 prototype에 선언해 두었던 goForward 함수를 호출해보자. 

 

띠용 undefined

 

 

왜 yellowCar.__proto__.goForward()로 프로퍼티를 잘 접근해서 호출하면 undefined가 뜰까...?

바로 goForward에서 this 키워드를 사용하는데 this는 .을 기준으로 바로 앞 객체를 가리키기 때문에 왜 yellowCar.__proto__.goForward()에서는 this가 yellowCar.__proto__를 가리켜 color 프로퍼티를 찾지 못했기 때문에 undefined가 뜬다. 반대로 yellowCar.goForwrad()는 this가 yellowCar를 가리켜 color 프로퍼티를 찾아 제대로 출력되는 것을 볼 수 있다.

(this를 아직 제대로 공부하지 못해서 내용이 틀릴 수 있음)

 

Car.prototype.goForward = function() { // 악셀 밟기
	console.log(this.color, "색상 차로 달려보자~");
}; 

 

 

근데 yellowCar.goForward()는 yellowCar에 goForward 프로퍼티가 없는데 왜 잘 작동하는 것일까?

 

프로토타입 체이닝

[[Prototype]] 내부에 다시 [[Prototype]]가 연쇄적으로 이어져 있어 이 것을 프로토타입 체인이라고 하며, 자바스크립트는 특정 프로퍼티에 접근할 때 해당 객체에 프로퍼티가 없으면 [[Prototype]] 내부 슬롯의 참조를 따라 상위 프로토타입의 프로퍼티를 순차적으로 검색하는 것을 프로토타입 체이닝이라고 한다.

 

아마 yellowCar 안에는 goForward 프로퍼티가 없기 때문에 프로토타입 체이닝을 따라 원본 객체의 portotype object 안에 있는 goforward 함수를 이용했을 거라고 생각한다... 근데 코어 자바스크립트 책에서는 그냥 __proto__를 생략해도 되고, 왜 생략해도 되는지 이해하지 말라 했던 것 같다. 

 

참고

반응형

'Frontend > Javascript' 카테고리의 다른 글

[javascript] 정규 표현식 정복하기  (0) 2024.06.29
[javascript] Export default?  (0) 2024.02.26
[Javascript] JS 기초 문법 정리  (0) 2024.02.19