본문 바로가기
Programming/자바스크립트

[자바스크립트] 프로토타입 - 개념

by 코딩하는 랄로 2023. 11. 4.
728x90

프로토타입 기반의 언어 : 자바스크립트

자바스크립트는 클래스 기반의 객체지향 언어가 아닌 프로토 타입 기반의 객체지향 언어이다. 그렇기 때문에 클래스라는 개념이 존재 하지 않는다. 여기서 생기는 의문점은 "그렇다면 자바스크립트에서 사용하는 class 키워드는 무엇이냐"이다.

 

자바스크립트의 class를 공부하다보면 "class지만 자바스크립트 엔진 내부에서는 prototype 타입 체이닝 방식과 똑같이 동작한다"는 말을 들어본 적이 있을 것이다. 즉, 클래스는 개념보다는 기존의 프로토타입 체이닝 방식을 자바의 클래스 방식을 참고하여 편하게 구현할 수 있도록 하는 "문법" 인 것이다.

 

그렇기 때문에 자바스크립트는 클래스라는 개념이 없고 프로토타입이라는 개념을 기반으로 하는 언어인것이다. 프로토타입은 기존의 객체를 복사하여 새로운 객체를 생성하는 개념이고 자바스크립트는 이러한 프로토타입을 이용하여 객체를 확장하고 객체 지향적인 언어를 구현하는 것이다.

 

 

 

함수와 객체의 내부구조

프로토타입에 대해서 알아보기 전에 먼저 자바스크립트에서 함수와 객체의 내부 구조를 먼저 살펴볼 필요가 있다. 먼저 객체를 생성하기 위한 생성자 함수를 정의하고 해당 함수를 통해 생성한 객체를 브라우저의 콘솔 창에 출력해보자.

위의 그림을 보면 생성된 객체가 프로토타입이라는 속성을 가지고 있다. 그리고 속성 내부에는 constructor라는 속성이 생성자 함수를 가리키는 것 또한 볼 수 있다.

 

여기서 헷갈리지 말아야 할 것은, Person() 생성자 함수를 통해 생성된 객체는 해당 생성자 함수를 참조하고 있는 것이 아닌 생성자 함수를 참조하고 있는 Person 프로토타입 객체를 참조하고 있는 것이다.

 

자바스크립트는 프로토타입 기반의 언어이기 때문에, 생성자 함수가 자바스크립트 엔진에 의해서 파싱될 때, 해당 함수를 constructor 속성으로 가지는 프로토타입 객체가 생성이 되고, 그 객체는 생성자 함수를 통해 생성된 모든 객체가 참조하게 되는 구조인 것이다.

 

그렇다면 위의 구조에서 constructor 속성을 들어가보면 그 안에 또 프로토타입 속성이 있고 해당 프로토타입은 function 객체를 constructor로 가지고 있다. 이것은 무엇을 의미하는 것일까?

 

바로 생성자 함수 또한 함수 객체를 참조하고 있고, 함수 객체가 가지고 있는 함수 객체 생성자를 통해 Person 생성자 함수가 정의되었음을 알 수 있다. 즉, 이렇게 기존의 생성된 프로토타입 객체를 이용하여 새로운 프로토타입 객체를 생성할 수 있고 이를 계속해서 참조(프로토타입 체이닝)하여 확장해 나가는 것이 자바스크립트 언어인 것이다.

 

 

 

프로토타입 객체

함수와 객체의 구조를 뜯어보면서 프로토타입 객체의 개념에 대해서 어렴풋이 무엇인지 느껴질 것이다. 이번에는 본격적으로 프로토타입 객체가 정확히 무엇인지에 대해 알아보겠다.

 

프로토타입 객체는 다른 객체의 원형이 되는 객체를 의미한다. 즉, 위의 예제에서 Function 객체는 Person 생성자 함수의 원형이 되는 객체인 것이다. 또한, Person 생성자 함수를 constructor로 갖는 프로토타입 객체가 new Person()을 통해 생성된 객체의 원형이 되는 객체임을 의미한다.

 

그렇기 때문에, 만약 해당 객체의 원형이 되는 객체를 알고 싶을 때에는 위와 같은 방식을 통해서 알 수 있는 것이다. 이렇듯 프로토타입 객체는 다른 객체의 원형이 되는 객체를 일컫고 모든 객체는 자신의 프로토타입 객체에 접근할 수 있다.  

 

또한, 프로토타입 객체는 동적으로 런타임에 멤버를 추가할 수 있고, 해당 객체를 원형으로 갖는 모든 객체는 이렇게 추가된 멤버를 사용할 수 있다.

function Person (name, age) {
    this.name = name;
    this.age = age;
}

const person = new Person('kim', 26);

//생성자 함수의 prototype은 생성자 함수를 constructor로 갖는
//프로토타입 객체를 참조하고 있다.
Person.prototype.printInfo = function() {
    console.log(`이름 : ${this.name}  나이 : ${this.age}`)
}

// 객체 생성 이후에 프로토타입 객체의 멤버가 추가되어도
// 사용가능
person.printInfo(); //이름 : kim  나이 : 26

 

 

프로토타입 객체의 멤버를 수정, 추가, 삭제를 할 때에는 주의해야 할 점이 있다. 이를 위해 다음의 예제 코드를 살펴보자

function Person (name, age) {
    this.name = name;
    this.age = age;
}

Person.prototype.printInfo = function() {
    console.log(`이름 : ${this.name}  나이 : ${this.age}`)
}

const person1 = new Person('kim', 26);
const person2 = new Person('lee', 27);

person1.printInfo = function() {
    console.log(`My name is ${this.name}`);
}

person1.printInfo(); // My name is kim
person2.printInfo(); // 이름 : lee  나이 : 27

 

지금까지의 내용을 이해했다면, 위의 결과가 무엇을 의미하는지 알 수 있어야 한다. person1의 printInfo는 프로토타입 객체의 printInfo 함수를 변경한 것이 아닌, person1 객체의 메소드로 추가한 것일 뿐이다. 그렇기 때문에 person2.printInfo를 호출하면 기존의 프로토타입의 printInfo가 호출되는 것이다.

 

이렇듯, 프로토타입 객체의 멤버를 추가, 수정, 삭제를 하기 위해서는 해당 객체를 생성하는 생성자를 갖는 프로토타입 객체에 접근하여야 한다.(위에 예제에서는 Person.prototype을 통해 접근 가능)

 

 

 

프로토타입

프로토타입 객체는 자신의 원형이 되는 객체를 프로토타입으로 가진다고 하였는데, 그렇다면 계속해서 원형이 되는 객체를 거슬러 올라간다면, 어떤 객체가 나올까? (분명, 무한히 거슬러 올라가지는 않을 것이다...)

 

바로, Object.prototype 이다. 자바스크립트에서 모든 객체의 최상위 객체는 Object.prototype이고 해당 객체의 프로퍼티, 메소드를 상속받는 것이다. 그렇다면, Object.prototyp부터 쭉 상속 받아 내려온 확장되어 생성된 이름 모를 객체가 자신의 원형이 되는 바로 상위의 객체에 대해서 알고 싶다면 어떻게 해야 할까?

 

브라우저 콘솔 창을 통해서도 볼 수 있지만, 해당 객체의 숨겨진 속성인 __proto__를 통해서 알 수 있다.

 

해당 속성은 자신을 만들어낸 바로 상위의 원형 객체를 참조하는 링크를 담고 있고, 이를 프로토타입이라고 정의한다. 이 숨겨진 속성 __proto__는 엄연히 객체가 가지고 있는 속성 prototype과는 다르다. 

 

prototype 속성은 프로토타입 객체를 참조하는 속성이고 함수와 new 연산자가 만나 생성한 객체의 프로토타입 객체를 지정해주는 역할을 한다. 반면에 객체 안의 숨겨진 __proto__ 속성은 자신을 만들어낸 원형이 프로토타입 객체를 참조하는 숨겨진 링크로써 프로토타입을 의미한다.

728x90