FrontEnd/JavaScript

[JS] ProtoType

환미니 2022. 5. 26. 02:08

 

Intro

mdn

자바스크립트를 공부하다 보면 한번씩 만나거나 mdn 문서를 보더라도 자주 만나는 개념이다 !

array 에 대해서만 mdn 에서 보더라도 prototype이 찍혀있다 ! 

 

객체지향 프로그래밍

 

프로토 타입에 들어가기 앞서서 객체지향 프로그래밍에 대한 개념에 대해 정리를 하면 좋은데 , 

객체의 집합으로 프로그램을 표현하는 프로그래밍 패러다임 이다.

객체는 각각 속성(프로퍼티)을 가지고 있기 때문에 다른 객체와 구별 해서 인식 할 수 있다.

예를 들어 홍길동 이란 사람은 키가 180 이며 몸무게가 78kg  서울 롯데 시그니엘 아파트 3001호 산다. 라고 하면 

이 홍길동이란 사람의 속성을 이용해서 다른 사람들과 구별 할 수 있는 것 입니다.

 

즉 객체지향 프로그래밍은 객체의 상태를 나타내는 데이터와 , 이 데이터를 조작할 수 있는 동작을 하나의 논리적인 단위로 묶은 것 이다.

사실 이게 말만 들으면 어려워서 .... ㅎㅎ

코드를 보면서 설명해 보겠다.

const hwanmin = {
  name: 'hwanmin',
  address: '광주 아무개 아파트',
  homeIn: function () {
    return `${this.name}이는 ${this.address}에 들어옵니다.`;
  },
  homeOut: function () {
    return `${this.name}이는 ${this.address}에 나갑니다.`;
  },
};


console.log(hwanmin.homeOut()); // hwanmin이는 광주 아무개 아파트에 나갑니다.
console.log(hwanmin.homeIn()); // hwanmin이는 광주 아무개 아파트에 들어옵니다.

즉 hwanmin 이라는 객체 상태 데이터인 name , address 를 사용해서 동작을 나타내는 하나의 논리적인 단위로 묶은 복합적인 자료구조 이다.

 

prototype 상속 

 

상속은 객체지향 프로그래밍 핵심 개념이다 !! 

즉 부모 클래스 상태 , 메서드 등을 그대로 상속 받아서 사용 가능한것 !! 

 

상속을 이용하면 불필요한 코드 중복을 제거할 수 있습니다 ! 

 

아래 예제에서 생성자 함수가 있다고 가정해 보겠습니다 .

 

<중복 발생!!>

function Human(name) {
  this.name = name;
  this.getName = function () {
    return `이름은 ${this.name} 입니다.`;
  };
}

const HwanMin = new Human('hwamin');
const Andrew = new Human('Andrew');

console.log(HwanMin.getName());
console.log(Andrew.getName());

위 함수는 치명적인 문제가 있습니다. 

 

HwanMin , Andrew 둘다 동일한 getName() 메서드를 포함하고 있습니다.

즉 불필요한 메서드 중복이 발생한 것 입니다. 

 

이러한 중복 을 피하기 위해서 protoype 개념이 필요해 집니다. 

 

<중복제거!!>

function Human(name) {
  this.name = name;
}

Human.prototype.getName = function () {
  return `이름은 ${this.name} 입니다.`;
};

const HwanMin = new Human('hwamin');
const Andrew = new Human('Andrew');

console.log(HwanMin.getName());
console.log(Andrew.getName());

 

즉 Human 생성자 함수로 생성한 모든 인스턴스는 Human.prototype의 모든 프로퍼티와 메서드를 상속 받는다!! 

 

그 이후에 인스턴스들은 Human.prototype 에 있는 메서드에 접근이 가능해 집니다 .

 

프로토타입 객체

prototype이 존재하는 이유는 객체간 상속을 구현하기 위해서 사용 됩니다.

 

생성자 함수는 prototype 속성을 갖고 있고 , 만들어진 생성자함수.prototype은 constructor를 가지고 있다.

function Human(name) {
  this.name = name;
}

Human.prototype.getName = function () {
  return `이름은 ${this.name} 입니다.`;
};

console.log(Human.prototype.constructor); // [Function: Human]

constructor를 사용해서 생성자 함수에 접근할 수 있다 ! 

 

모든 객체는 -> __proto__ 접근자 프로퍼티를 사용해서 생성자함수.prototype 에 접근할 수 있다.

 

let hwanmin = {
  name: 'hwammin',
};

console.log(hwanmin.hasOwnProperty('__proto__')); //false
console.log(Object.prototype.hasOwnProperty('__proto__')); //true

위 예시를 보면 객체가 직접 __proto__ 접근자 프로퍼티를 얻는게 아니라 , Object.prototype의 프로퍼티를 얻는 것 이다.

 

정리 

__proto__ 접근자 프로퍼티 : Object.prototype을 상속받은 모든 객체가 가진다.

prototype 프로퍼티 : 생성자 함수 프로퍼티는 생성자함수.prototype 의 constructor를 가르킨다. 

 

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

const me = new Person('Hwan');

console.log(Person.prototype === me.__proto__); //true
console.log(me.constructor === Person); // true

즉 true 가 나오는 이유는 me.__proto__ 접근자 프로퍼티로 Person.prototype에 접근 했기 때문이다.

me.constructor === Person인 이유는 처음에 me 인스턴스가 생성될 때 Person.prototype을 상속 받는데 , 

이때 Person.prototype 은 constructor 로 생성자 함수를 가르킨 채로 상속되기 때문에 두번째 true가 나오는 이유이다. 

 

📌 요약

1. 생성자 함수를 만들면 생성자함수.prototype 객체가 생성되고 , 이 객체는 constructor 로 생성자 함수를 가르킨다.

 

2. 생성자 함수를 이용해서 인스턴스를 만들면 __proto__ 접근자 프로퍼티로 생성자함수.prototype에 접근이 가능하다.

 

3. prototype 객체는 자식 인스턴스에 상속이 일어난다. 그래서 자식 instance 에서 constructor 를 사용하면 생성자 함수를 가르키는걸 확인 할 수 잇다.

 

그래서 mdn에 있는 Array.prototype.length 는 Array 를  상속받는 모든 인스턴스가 사용 가능한게 되는 것 이다.

 

🌟 Bonus (Super Array) 만들기

 

1. 제일 긴 글자 길이를 반환해주는 maxLength() 메서드 만들기 !

2. 중복된 배열 요소를 제거해준 removeOverlap() 메서드 만들기 !

class SuperArray extends Array {
  maxLength() {
    let max = this[0];
    for (let i = 1; i < this.length; i++) {
      if (max.length < this[i].length) {
        max = this[i];
      }
    }
    return max.length;
  }
  removeOverlap() {
    let set = new Set(this);
    return new Array(...set);
  }
}

const superArray = new SuperArray(
  'HwanMin',
  'HwanMin',
  'SuperHwanMin',
  '안녕하세요반갑습니다ㅎㅎ!!!!'
);

console.log(superArray.maxLength()); 16

console.log(superArray.removeOverlap());[ 'HwanMin', 'SuperHwanMin', '안녕하세요반갑습니다ㅎㅎ!!!!' ]

각각 요소들은 부모 클래스인 Array를 상속받아서 만들었습니다. 

 

부모 요소를 상속받은 SuperArray 클래스는 Array의 모든 메서드를 가져다 쓸 수 있고 , 

추가적으로 커스텀으로 만든 메서드 사용이 가능해 집니다 ! ! 🛠