[TIL] 프로토타입

Kyoorim LEE·2023년 9월 13일
0

스스로TIL

목록 보기
29/34

프로토타입

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

클래스 기반 언어에서는 '상속'을 사용하지만 프로토타입 기반 언어에서는 어떤 객체를 원형(prototype)으로 삼고 이를 복제(참조)한다. 결국 '상속'과 비슷한 개념이라고 볼 수 있다.

1. 프로토타입 개념 이해

constructor, prototype, instance

  1. prototype은 객체다. 객체 내부에는 인스턴스가 사용할 메서드를 저장한다
  2. 어떤 생성자 함수(Constructor)를 new 연산자와 같이 호출한다
  3. Constructor에서 정의된 내용으로 새로운 instance가 생성된다
  4. 이때 instance에는 __proto__라는 프로퍼티가 자동 부여된다
  5. 이 프로퍼티는 Constructor의 prototype이라는 프로퍼티를 참조한다
  6. protytpe을 참조하고 있는 __proto__역시 객체다
  7. 인스턴스의 숨겨진 프로퍼티인 __proto__를 통해 prototype에 저장되어있는 메서드에 접근한다
  8. __proto__를 읽을 때는 'dunder proto'라고 발음하면 된다. dunder는 double underscore의 줄임말이다

예제를 통해 살펴보자

var Person = function (name) {
	this._name = name;
};

Person.prototype.getName = function() {
	return this._name;
};

var sia = new Person('Sia');

sia.__proto__.getName(); // undefined ----(1)
Person.prototype === sia.__proto__ // true -----(2)

(1) 메서드 결과로 왜 undefined가 나왔을까? 에러가 뜨지 않았다는 것은 곧 이 변수가 '호출할 수 있는 함수'라는 뜻이다. 만약 실행할 수 없는 함수라면 TypeError가 발생했을 것이다. 따라서 getName이 함수라는 것이 입증됐다.

일단 함수라는 것은 확인했고,

그렇다면 왜 undefined가 나온건지 살펴보자. 문제는 this에 바인딩된 대상이 잘못되었기 때문이다.

어떤 함수를 '메서드로서' 호출할 때는 메서드명 바로 앞의 객체가 곧 this가 된다. 즉 sia.__proto__.getName()에서 getName 함수 내부에서의 thissia가 아니라 sia.__proto__라는 객체라는 것이다. 이 객체 내부에 name 프로퍼티가 없으므로 찾고자하는 식별자가 정의되어있지 않기 때문에 undefined를 뱉어낸 것이다.


만약 위 예제가 아래와 같이 바뀐다면 ?

var sia = new Person('Sia');
sia.__proto__._name = 'SIA__proto__'
sia.__proto__.getName(); // SIA__proto__

결국 관건은 this였다!

this를 인스턴스로 해서 __proto__없이 인스턴스에서 곧바로 메서드를 쓰면 좋을 것 같다.

var sia = new Person('SIA', 03);
sia.getName(); // SIA

var moa = new Person('MOA', 04);
moa.getName(); // MOA

어떻게 __proto__ 없이 바로 되지?

그 이유는 바로 __proto__가 생략 가능한 프로퍼티이기 때문이다. 원래부터 생략 가능하도록 정의되어있었고 이 정의를 바탕으로 자바스크립트의 전체 구조가 구성된 것이다.

sia.__proto__.getName
-> sia(.__proto__).getName
-> sia.getName

위 예제에서 __proto__를 생략하지않고 그대로 쓰면 thissia.__proto__를 가리키지만, 이를 생략하면 sia를 가리킨다.


a. constructor 프로퍼티

생성자 함수의 프로퍼티인 prototype 객체 내부에 constructor라는 프로퍼티가 있다. 이 프로퍼티는 원래의 생성자 함수(자기 자신)을 참조한다. 이 프로퍼티는 인스턴스와의 관계에서 꼭 필요한 정보다.

var arr = [1, 2];
Array.prototype.constructor === Array // true
arr.__proto__.constructor === Array // true
arr.constructor === Array // true

var arr2 = new arr.constructor(3, 4); // - A
console.log(arr2); // [3, 4]
  • 인스턴스의 __proto__가 생성자 함수의 prototype 프로퍼티를 참조하며 __proto__가 생략 가능해졌다.
  • 이로 인해 인스턴스에서 직접 constructor에 접근할 수 있게 됐다.
  • A번째 줄이 오류 없이 잘 동작하는 이유가 결국 바로 이 __proto__ 덕분!


2. 프로토타입 체인

직각삼각형의 대각선 방향, 즉 __proto__ 방향을 계속 찾아가면 최종적으로는 Object.prototype에 이르게 됨.

이런 식으로 __proto__ 안에 다시 __proto__를 찾아가는 과정을 프로토타입 체이닝이라고 함.

프로토타입 체이닝을 통해 각 프로토타입 메서드를 자신의 것처럼 호출할 수 있음.

profile
oneThing

1개의 댓글

comment-user-thumbnail
2023년 9월 14일

proto를 dunder proto 라고 읽는군요 ㅋㅋ
프로토타입과 클래스는 실제 업무할 때 언제 쓰는건지 항상 궁금한데 아직 답변을 못찾았어요.. use cases도 같이 소개해주세요!

답글 달기