TIL. 프로토타입

chloe·2021년 1월 15일
0

TIL

목록 보기
36/81

👩‍💻 프로토타입이란?

자바스크립트는 프로토타입 기반 언어이다.
클래스 기반 언어에서는 상속을 사용하지만 프로토타입 기반 언어에서는 어떤 객체를 원형(프로토타입)으로 삼고 이를 복제(참조)하여 상속과 비슷한 효과를 얻는다.

위 그림은 아래 코드를 추상화한 것이다.

var instance=new Constructor();

왼쪽(실선)의 꼭짓점에는 Constructor(생성자 함수)를 오른쪽 꼭지점에는 Constructor.prototype이라는 프로퍼티를 위치시켰다.
왼쪽 꼭짓점으로부터 아래를 향한 화살표 중간에 new가 있고, 화살표의 종점에는 instance가 있다. 오른쪽 꼭짓점으로부터 대각선 아래로 향하는 화살표의 종점에는 instance._proto_라는 프로퍼티를 위치시켰다.

  • 어떤 생성자 함수Constructor를 new 연산자와 함께 호출하면
  • Constructor에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성된다
  • 이때 instance에는 __ Proto__라는 프로퍼티가 자동으로 부여되는데,
  • 이 프로퍼티는 Constructor의 Prototype이라는 프로퍼티를 참조한다.

👉 프로토타입의 핵심 개념 : Prototype프로퍼티, __ Proto__ 프로퍼티

둘은 객체를 의미한다. prototype 객체 내부에는 인스턴스가 사용할 메서드를 저장한다. 그러면 인스턴스에서도 숨겨진 프로퍼티인 __ Proto__를 통해 이 메서드들에 접근할 수 있게 된다.

예를들어, `Person이라는 생성자 함수의 prototype에 getName이라는 메서드를 지정했다.

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

이제 Person의 인스턴스는 __Proto__프로퍼티를 통해 getName을 호출할 수 있다.

var suzi=new Person('Suzi')
suzi.__Proto__.getName();//undefined
Person.prototype ===suzi.__proto__//true

왜냐하면 instance의 __Proto__가 Constructor의 prototype프로퍼티를 참조하므로 결국 둘은 같은 객체를 바라보기 때문이다.

  • 어떤 변수를 실행해 undefined가 나왔다는 것은 이 변수가 '호출할 수 있는 함수'에 해당한다는 것을 의미한다. 즉 getName은 함수다.

어떤 함수를 메소드로서 호출할 때는 메서드명 바로 앞의 객체가 곧 this가 된다.
즉,thomas.__proto__.getName()에서 getName 함수 내부에서의 this는 thomas가 아니라 thomas.__proto__라는 객체가 되는 것이다.

var suzi=new Person('Suzi',28);
suzi.getName();//Suzi
var iu=new Person('Jieun',28);
iu.getName();//Jieun

__proto__는 생략가능한 프로퍼티

suzi.__proto__.getName
->suzi(.__proto__).getName
-> suzi.getName

__Proto__를 생략하지 않으면 this는 suzi.__proto__를 가리키지만, 이를 생략하면 suzi를 가리킨다.

🙂 다시 프로토타입을 정의해보자면 "new 연산자로 constructor를 호출하면 instance가 만들어지는데, 이 instance의 생략 가능한 프로퍼티인 __proto__는 constructor의 prototype을 참조한다~!"

💁‍♂️ Constructor 프로퍼티

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);
console.log(arr2);//[3,4]

인스턴스의 __proto__가 생성자 함수의 prototype프로퍼티를 참조하며 __proto__가 생략가능하기에 인스턴스에서 직접 constructor에 접근할 수 있는 수단이 생긴 것이다.

var Person=function(name){
  this.name=name;
};
var p1=new Person('사람1');
var p1Proto=Object.getPrototypeOf(p1);
var p2= new Person.prototype.constructor('사람2');
var p3= new p1Proto.constuctor('사람3');
var p4= new p1.__proto__.constructor('사람4');
var p5=new p1.constructor('사람5');

[p1,p2,p3,p4,p5].forEach(function(p){
  console.log(p,p instanceof Person);
});

p1에서 p5까지는 모두 person의 인스턴스이다.

프로토타입 체인

1. 메서드 오버라이드

prototype 객체를 참조하는 __proto__를 생략하면 인스턴스는 prototype에 정의된 프로퍼티나 메서드를 마치 자신의 것처럼 사용할 수 있다고 했다. 그런데 만약 인스턴스가 동일한 이름의 프로퍼티 또는 메서드를 가지고 있는 상황이라면?

var Person=function(name){
  this.name=name;
};
Person.prototype.getName=function(){
  return this.name;
};
var iu=new Person('지금');
iu.getName=function(){
  return '바로'+this.name;
};
console.log(iu.getName());//바로 지금

iu.__proto__.getName이 아닌 iu객체에 있는 getName메서드가 호출됐다. 여기서 일어난 현상을 메서드 오버라이드라고 한다. 메서드 위에 메서드를 덮어씌었다는 표현이다.

자바스크립트 엔진이 getName이라는 메서드를 찾는 방식은 가장 가까운 대상인 자신의 프로퍼티를 검색하고, 없으면 그 다음으로 가까운 대상인 __proto__를 검색하는 순서로 진행된다.

profile
Front-end Developer 👩🏻‍💻

0개의 댓글