프로토타입

SeungMin·2022년 11월 8일
0

JAVA SCRIPT STUDY

목록 보기
9/9

프로토타입?

자바스크립트는 다른 언어들:클래스 기반 과는 다르게
프로토타입 기반 언어이다.

이 글을 작성하는데 참고한 책에서는 프로토타입을
아래의 그림만 이해하면 모두 이해 한 것이라고 설명한다.

위의 추상도는 아래의 코드를 기반으로 그린 것이라는데

var instance = new Constructor();

해당 코드를 기반으로 그림을 조금 더 구체화 시키면 아래와 같게 된다.

즉 어떤 생성자 함수를 new 연산자와 함께 호출하면
생성자 함수에서 정의된 내용을 바탕으로 새로운 instance가 생성되는데

이때 instance에는 __proto__ 라는 프로퍼티가 자동으로 할당되며
이는 생성자 함수의 프로퍼티인 prototype을 참조한다 라는 뜻이다.

코드로 나타내면 아래와 같다.

var instance = new Constructor();

var isTrue = instance.__proto__ === Constructor.prototype;

console.log(isTrue); // true

그럼 생성자 함수AprototypegetName 메소드를 정의하면

A의 인스턴스인 instanceprototype을 참조하는 __proto__를 통해서
getName을 사용할 수 있을까?

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

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

var gildong = new Person('홍길동');
gildong.__proto__.getName(); // undefined

사용을 해본 결과 undefined가 출력된다.

만약 getName 자체가 정의되지 않은 상태라면
undefined가 아닌 typeError를 반환해야 맞다.

그렇다면 getName__proto__의 프로퍼티에 존재하고
출력된 this.name의 값이 undefined라고 보는 것이 맞다.

순서대로 파악을 해보면 gildong.__proto__.getName을 호출할 때의 주체는
엄밀히 따지고 보면 gildong이 아닌 gildong.__proto__ 이다

해당 객체의 프로퍼티에는 name이 정의되어있지 않기 때문에 undefined가 반환된다.

gildong.__proto__.name = "홍길동"

gildong.__proto__.getName(); // "홍길동"

name을 지정 해주면 제대로 반환된다.

즉 우리가 생각한 this와 프로토타입의 this가 다르다는걸 알 수 있다.

그럼 우리가 생각한 this를 바인딩하여 실행하는 방법이 뭘까?

답은 간단히도 .__proto__를 생략하면 된다.

var gildong = new Person('홍길동');
gildong.getName(); // '홍길동'

사실 .__proto__는 원래부터 생략이 가능하도록 정의 되어있다.
그 이유는 딱히 없다.

자바스크립트를 만들고 전체 구조를 설계한 브랜든 아이크의 머릿속에서 나온 개념이다.
책에서도 "그러니 그냥 그런가보다 하는 수 밖에 없다"고 설명한다.

이렇게 생략을 하게되면 위에서 설명한 그림이 아래와 같이 바뀐다.

.__proto__ 를 생락하면
원본 생성자 함수의 prototype 에 정의된 메서드나 프로퍼티에 접근할 수 있게된다.


그럼 아래의 코드를 보자

var arr = [1,2];
console.log(arr.__proto__ === Array.prototype) // true

평소처럼 배열을 만들면 자연스럽게 Arrayinstance로써 만들어지는걸 알 수있다.

그럼 Array.prototype의 메서드는 당연히 arr이 본인의 것 처럼 사용할 수 있어야 맞다.

그런데 Array의 메소드중 isArray를 보면 사용하는 방법이 조금 다르다.

var arr = [1,2];
console.log(Array.isArray(arr)); // true

isArray를 본인의 메서드인듯 사용할 수 있었다면

arr.isArray() 이렇게 사용해야 맞다.

뭐가 다른걸까?

이유는 isArrayArray의 정적 메소드이기 때문에 prototype의 프로퍼티가 아니다.

instance.__proto__가 참조하는 것은 원본 생성자의 prototype 뿐이다.

prototype의 프로퍼티보다 상위에서,
Array의 프로퍼티로 선언된 정적 메소드는 사용법이 다를 수 밖에 없다.


constructor 프로퍼티?

생성자 함수.prototype 의 프로퍼티중에는 constructor 라는 프로퍼티가 있다.
물론 prototype을 참조하는 __proto__에도 있다.

이 프로퍼티는 단어 그대로 원본 생성자 자신을 참조하고 있다.
그리고 그 역할은 instance가 자신의 원본이 무엇인지 알 수있는 수단이다.

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]

arr2를 보면 arr.constructorArray를 참조하고 있다.
그리고 new 연산자로 instance를 만든 형태이기 때문에

var arr2 = new Array(3,4); 

위와 동일하다고 볼 수 있다.


프로토타입 체인


메서드 오버라이드?

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

var gildong = new Person('홍길동');
gildong.getName = function(){
  return '나는' + this.name;
}
console.log(gildong.getName()); // 나는홍길동

원래라면 gildongPersoninstance이므로
gildong.getName()Person.prototype을 참조하여
Person.prototype.getName()을 호출하지만

__proto__ 프로퍼티와 같은 레벨에서 getName을 오버라이드 했기 때문에
Person.prototype.getName()이 아닌 gildong.getName()을 호출한다.

때문에 thisinstance === gildong으로 바인딩되며

'나는' + this.name === '나는' + gildong.name === '나는' + '홍길동'
최종적으로 "나는홍길동" 이 출력된다.


프로토타입 체인?

사실 어떤 생성자의 prototype 안을 살펴보면 __proto__가 한번 더 나오는걸 볼 수 있다.

그 이유는, 모든 protyotype객체이기 때문이다.

사진을 보면 어떤 생성자 함수의 prototype은 결국
Object.prototype을 참조하는 일종의 __proto__와 같다는걸 알 수 있다.

그렇다면 Object.prototype에 사용자 지정 메서드를 추가하거나 오버라이드 하면
결국 최종적으로 프로토 타입 체인을 통해서 해당 메서드에 접근하여 사용할 수 있는가?

사용할 수 있다.

Arrayinstancevar a에 할당하여
Array.prototype.__proto__.abc.call(a)의 형태로 호출된것.


다중 프로토타입 체인?

var Grade = function(){
  var args = Array.prototype.slice.call(arguments);
  for(var i = 0; i < args; i++){
    this[i] = args[i];
  }
  this.length = args.length;
}

var g = new Grade(100,80);

이렇게 되면 g.length 또는 g[number]와 같은
Grade.prototype에 접근하여 실행할 수 있는 메서드나 프로퍼티는 접근이 가능하지만

Array와는 연결 되어있지 않기 때문에
Array.prototype의 메서드나 프로퍼티에는 접근할 수 없게된다.

그러기 위해서는 g.__proto__Grade.prototype
Arrayinstance를 바라보게 하면 된다.

Grade.prototype = [];

그럼 Array.prototype의 메서드에 접근할 수 있게 된다.

g.push(1); // [100,80,1]

g.shift(); // [80,1];

g.unshift(2); // [2,80,1];

즉 아래와 같은 형태로 프로토타입 체인이 연결된 것.

최종적으로는 아래와 같은 형태가 된다.

!(다중 프로토타입 체인2)[https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Fk.kakaocdn.net%2Fdn%2FbnpsKc%2FbtqRQSKjkEP%2Fsu5x8x8eXqKMeZj74Kzci0%2Fimg.png]

profile
공부기록

0개의 댓글