코어 자바스크립트(프로토타입)

L·2022년 12월 21일
0

코어자바스크립트

목록 보기
6/6
post-thumbnail

프로토타입의 개념 이해

자바스크립트는 프로토 타입의 언어. 객체지향 언어에서는 상속을 사용하지만, JS에서는 prototype 을 기반으로, 이를 복제(참조)하여 상속과 비슷하게 흉내 낼수 있음.

1. constructor,prototype,instance

  • 선언 방법
const Person = function(name){
  this.name = name;
}
Person.prototype.getName = function(){
  return this.name;
}
var LEE = new Person('Lee');
console.log(Person.prototype === LEE.__proto__) // true
console.log(LEE.__proto__.getName()) // undefined
console.log(LEE.getName()) // Lee
        
  • LEEnew 연산자를 통해 Person함수를 호출 시 정의된 내용을 바탕으로 인스턴스가 생성이 되면서, 인스턴스에는 __proto__([[prototype]]) 가 자동적으로 부여가 됨. 이 값은 Personprototype 프로퍼티를 참조 함.
  • 위에서 인스턴스의 __proto__Constructorprototype를 참조하기 때문에 true를 출력함.
  • 인스턴스에서 getName()메소드를 접근하려면 LEE.__proto__.getName()으로 접근시 undefined가 발생함. JS의 규약에선 __proto__는 생략이 가능한 프로퍼티기 때문에 LEE.getName()을 통해 호출이 가능.(메서드 호출시 thisLEE를 가리키기 때문에 Lee값을 바라보게 됨)

    LEE.__proto__ 와 같은 방식으로 접근은 허용되지않고, Object.getPropertyOf(인스턴스 변수명)/Refelect.getPropertyOf(인스턴스 변수명)/Object.create() 등을 통해서만 접근.

위 캡쳐는 배열의 결과를 출력한 결과, 아래는 Array 생성자 함수를 출력한 결과.

  • 위 캡쳐에 있는 배열은 Array라는 생성자 함수를 바탕으로 생성이 되었음을 알수있음.
  • [[Prototype]]를 열어보면, join,concat,filter등 배열에서 사용할수 있는 메서드들이 들어가 있는데, Array 생성자 함수에도 똑같이 들어가 있음을 알수가 있음.
  • 즉, 인스턴스의 [[Prototype]]Array.prototype을 참조하는것을 알수있음
  • 그리고, 위에서 __proto__은 생략이 가능하기 때문에 [1,2,3].filter... 등의 메서드를 자신의 것처럼 사용가능.
  • 반면 from,isArray메서드는 prototype내부에 없기때문에 인스턴스가 호출할수 있음.

생성자 함수의 prototype에 어떤 메서드나 프로퍼티가 있다면 인스턴스에서도 마치 자신의것처럼 생성자 함수의 prototype의 메서드나 프로퍼티를 접근할수 있다.

  • number,string 리터럴은 객체가 아닌데, prototype의 메서드를 사용할수 있는 이유가 있다. 예를 들어 10.toFixed(2)를 사용할때는, 자바스크립트는 number 생성자 함수의 인스턴스를 임의로 만들어서 프로토타입에 있는 메서드를 적용해서 결과를 얻게 한다음, 인스턴스를 제거하는 방식으로 진행이 된다.
  • 참조형(Object,Array)는 처음부터 인스턴스이기 때문에, 이런 복잡한 과정을 거치진않음.
  • 기본적으로 null,undefined를 제외한 모든 데이터 타입은 생성자 함수가 존재하며, 각 생성자 함수의 prototype은 데이터 타입에 맞는 메서드들이 있다.

2.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];

arr__proto__Array의 생성자 prototype을 참조하기 때문에, Array의 constructor와 인스턴스의 constructor는 동일하다고 나옴. 즉, constructor은 생성자 함수(자기 자신)을 참조하기때문에, Array와 동일하다는 결과가 나옴.

var Person = function(name){
  this.name = name;
}
var p1 = new Person('사람1');
var p1Proto = Object.getPropertyOf(p1);
var p2 = new Person.prototype.constructor('사람2');
var p3 = new p1Proto.constructor('사람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);
}
/*
{name:'사람1'} true
{name:'사람2'} true
{name:'사람3'} true
{name:'사람4'} true
{name:'사람5'} true
*/
  • p1Person생성자 함수의 인스턴스 ([Constructor])
  • p2Person 생성자 함수를 참조하는 constructor이기 때문에 Person의 인스턴스임.([Constructor].prototype)
  • p3__proto__의 생성자를 참조하는 constructor 이기 때문에 Person의 인스턴스임.(Object.getPropertyOf(instance))
  • p4Person 인스턴스의 프로토 타입(__proto__)을 참조하는데 이는 Person.prototype이고, 이것에 constructorPerson 생성자 함수이므로, Person의 인스턴스임.(instance.__proto__ === Person )
  • p5p1constructor 즉 생성자 함수 그자체이기 때문에 Person의 인스턴스임.(instance.rototype)

2.프로토타입 체인

1.메서드 오버라이드

오버라이드란?
부모 클래스에서 정의된 함수를 상속 받은 자식 클래스에서 리턴타입,함수명,매개변수가 같은 함수로 재정의하여 사용.
책에서는 오버라이드는 다른 대상으로 교체하는것이 아닌 원본이 그대로 있는 상태에서 새로 정의한 내용을 위에 얹었다고 표현했음.

var Person = function(name){
  this.name = name;
}
Person.prototype.getName = function(){
  return `Hello ${this.name}`;
}

var IU = new Person('아이유');
IS.getName = function(){
  return `안녕하세유.. 저는 ${this.name} 이에유..`;
}
console.log(IU.getName());

위 소스에서 Person라는 생성자 함수에서 getName()을 호출하면 Hello name이 나오지만, IU라는 생성자 인스턴스를 선언하고, 생성자 인스턴스에 getName메서드를 재 정의후, 함수 호출시 다른 내용이 나옴.

책에서는 원본이 그대로 있는 상태에서 다른 대상을 그 위에 얹는다고 했을까?(개인적 생각)

  • 위 소스에서 IU.__proto__.getName()을 호출하면, Hello undefined가 나온다. 만약에 교체가 된다고하면, 이 결과도 안녕하세유.. 저는 undefined 이에유.. 가 나왔을것이다. 여기서 IU.__proto__.name = '배수지를 실행 후, 재 호출시 Hello 배수지가 나옴. 그 다음 IU.getName()을 출력하면 예상했던 결과대로 나옴.

2.프로토타입 체인



배열 [1,2]에 대한 구조를 콘솔로 살펴보면 인스턴스의 __proto__에는 Array.prototype를 참조하고, Array__proto__를 살펴보면 constructor(생성자 함수 참조)가 Object이고, toString(),ValueOf()등의 Object에서 사용하는 메서드들이 있음을 확인할 수 있음.

  • 기본적으로 모든 객체의 __proto__의 맨 마지막은 Object.prototype가 연결되어있는데, 이처럼 __proto__ 프로퍼티 내부에 다시 __proto__ 프로퍼티가 연쇄적으로 이어지는 것을 프로토타입 체이닝이라고 함.
  • 프로토타입 체이닝은 스코프 체인과 유사하게 해당 인스턴스 또는 프로토타입에 메서드가 없으면, 상위 __proto__의 프로퍼티에서 메서드를 찾고 없으면 오류를 출력.

정리

  • 생성자 함수를 new 연산자와 함께 호출하면 생성자 함수에서 정의된 내용을 바탕으로 새로운 인스턴스가 생성.
  • 인스턴스에는 __proto__ 라는 생성자 prototype을 참조하는 프로퍼티가 있음.
  • __proto__는 생략이 가능하며 Constructor.protype의 메서드를 자신이 선언한 메서드처럼 사용 가능.
  • Constructor.prototype 에는 constructor라는 프로퍼티가 있는데 이는 생성자 자기자신을 가리킴.
  • 인스턴스서 시작해서 __proto__ 안에 다시 __proto__로 끝까지 올라가면 최종적으로 Object.prototype가 있는데, 이를 프로토타입 체이닝이라고 함.

0개의 댓글