# 08. 프로토타입(Prototype)

## 프로토타입 (Prototype)

### 들어가며

JS는 다른 언어 Java, Python과 같이 관련된 것들을 묶어 class 내용을 바탕으로 인스턴스를 생성하는데 JS에는 class라는 개념이 존재하지 않습니다. 대신 프로토타입을 기반으로 객체를 생성, 상속, 모듈화 할 수 있습니다.\
ES6에는 class가 등장하였으나 결국 프로토타입 기반으로 동작을 합니다.

### 프로토타입은 어떻게 만드나요?

프로토타입을 선언하는 방법은 아래와 같습니다.

```javascript
대상.prototype.메서드이름 = function(){ // do Something... }
```

### 프로토타입이 뭐에요 ?&#x20;

모든 객체는 생성이 될 때 자신을 만든 객체와의 연결된 링크를 가지고 있는데 이것이 프로토타입입니다. \
두 가지 종류의 프로토타입이 존재하는데 크게 두 가지를 묶어 프로토타입이라고 부릅니다.

1. 자신을 통해 만들어질 객체들에게 정보를 전달해줄 연결에 대한 속성 (prototype property)
2. 누구를 통해 생성되었는지, 즉 자신을 만든 객체와 연결된 링크 (prototype link)

알게모르게 프로토타입을 이용해 개발을 해보셨을 거에요&#x20;

```javascript
var arr = [1, 2, 3, 4];
arr.join() // 1,2,3,4
```

arr.join() 이라는 함수를 사용하여 배열을 문자로 변경하였습니다. 그런데 생각해보면 우리는 join() 이라는 함수를 정의한 적 없습니다. 어떻게 이런 일이 가능 한 걸까요 ?\
\
개발자 도구에서 arr. 을 입력해보시면 이미 정의되어있는 수많은 메서드들과 속성들이 보입니다.\
(여기서 length 를 속성, concat() 들과 같은 함수를 메서드라 부릅니다)

```javascript
var arr = [1, 2, 3, 4];
console.dir(arr)
```

![arr 가 가지고 있는 메서드 목록](https://3676071105-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LNPupV2z3QokH-IFxbX%2F-LS8CWLXyxYci63J5KOo%2F-LS8EE3aGO6dBZdJ40xu%2Fimage.png?alt=media\&token=75606eab-1613-4161-9143-28e7464c3c50)

arr 을 조금 더 자세히 살펴보겠습니다.\
아래 사진에  \_\_proto\_\_ 가 arr 에 연결된 프로토타입 영역입니다. 정확히 말하자면 Array Object 가 가지고 있는 prototype property 입니다.&#x20;

arr 는 Array Object를 이용해서 만들어졌단 것을 확인할 수 있습니다 '.\_\_proto\_\_: Array' 라고 보이는 것이 Array Object 가 자신을 통해 만들어진 객체들에게 정보전달을 해주기 위한 prototype property 입니다.

![](https://3676071105-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LNPupV2z3QokH-IFxbX%2F-LS8CWLXyxYci63J5KOo%2F-LS8ENZIHD4k1re_D6Ye%2Fimage.png?alt=media\&token=5e4ba12e-24f7-4e82-ab4c-b59b0ce662c6)

Array Object의 \_\_proto\_\_ 영역안 쪽에 또 다른 \_\_proto\_\_ 가 존재하고있는 것을 볼 수 있습니다. Object를 공부하면서 기본 타입을 제외한 모든 것은 Object 라고 했습니다. \
Array Object 는 Object 를 통해 만들어졌고 연결된 링크(프로토타입) 를 가지고 있다는 것을 알 수 있습니다.

![](https://3676071105-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LNPupV2z3QokH-IFxbX%2F-LS8CWLXyxYci63J5KOo%2F-LS8ET9N42xRXpnuDNu-%2Fimage.png?alt=media\&token=95a2431c-b772-4ffa-8dbb-4454c62f2be4)

Object를 공부하면서 기본 타입을 제외한 모든 것은 Object 라고 했습니다.\
Array Object 또한 Object 로 만들어져 있다는 것을 알 수 있고, Object의 프로토타입과 연결되어 있는 것을 볼 수 있습니다.

arr => Array Object => Object  에 대한 정리를 해보겠습니다.

1. arr 는 Array Object를 통해 만들어졌습니다.
2. Array 가 자신을 통해 만들어진 객체들(ex. arr)에게 정보를 전달하기 위한 prototype property 를 가지고 있습니다.
3. Array Object 도 자신을 만든 Object 와의 프로토타입 링크를 가지고 있습니다.

### 그런데 프로토타입을 쓰면 뭐가좋아요?

```
사람 객체를 만들고 싶어요. 사람은 이름을 가질 수 있고, 자신의 이름을 말할 수 있습니다.
음 .. 한 10명정도 있었으면 좋겠어요 (철수, 짱구, 맹구 ..... 등)
```

위와 같은 요구사항이 들어왔습니다. 그래서 우리는 10 명의 사람 객체를 만들어야해요.

```javascript
var person = {
    name: '짱구', // 기본적으로 이름을 가집니다.
    getName: function () { // 자신의 이름을 말하고 return 해주는 메서드입니다.
        console.log('this.name')
        return this.name
    }
}
var person2 = {
    name: '철수', // 기본적으로 이름을 가집니다.
    getName: function () { // 자신의 이름을 말하고 return 해주는 메서드입니다.
        console.log('this.name')
        return this.name
    }
}
// .... person10
```

등등등... person10 까지 10개의 사람을 만들었습니다. 그런데 늘 그렇듯이 요구사항이 바뀌어버렸습니다.&#x20;

```
아무래도 사람은 30명정도 있는게 좋을거 같아요. 30명으로 늘려주세요
```

위처럼 person 을 30 까지 늘리는 것은 비효율적이라 생각하여 우리는 생성자 함수를 이용하는 방법을 바꿨습니다.&#x20;

```javascript
var Person = function(name) {
    this.name = name;
    this.getName = function () {
        console.log(this.name);
        return this.name;
    }
}
var person1 = new Person('철수')
var person2 = new Person('맹구')
person1.getName(); // 철수
person2.getName(); // 맹구
// ... person 30
```

사람수가 늘어도 new Person 을 통해서 생성하면 빠르게 생성할 수 있고 변경점이 생겨도 Person 생성자함수만 수정하면되니 생각보다 괜찮아 보입니다.

여기서 한가지 고민거리가 생깁니다. name 속성이야 모든 사람마다 이름이 다르기 때문에 각각 생성되는 객체에서 가지는게 맞는데 getName 메서드는 생성된 이름을 console 에 입력해주고 밖으로 이름을 return 해주는 역할로 고정되어있습니다.&#x20;

현재 상태를 살펴보겠습니다.&#x20;

![new Person 으로 생성된 person1, person2](https://3676071105-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LNPupV2z3QokH-IFxbX%2F-LS9ZQyJTSY301luU7N1%2F-LS9Mm4SaCAriXmjaNGE%2F%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202018-11-25%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%207.16.25.png?alt=media\&token=6a94ad8a-991b-4980-a53d-982d314ee48d)

무엇이 문제일까요 ???

person1 과 person2 의 정보를 console 에 찍어보니 두 객체 모두 name과 getName 메서드를 가지고 메모리에 올라가있습니다.그림으로 살펴보면 아래와 같습니다. \
Person 을 통해 생성된 person1 \~ person30 까지의 모든 객체가 name과 getName 을 가지고 메모리에 올라가버린 것 입니다. name 의 경우 각각의 속성이기 때문에 올라가는것이 맞으나 getName 의 경우는 같은 역할을 하는 메서드가 30개나 메모리에 올라가버린 것 입니다.&#x20;

![person1\~30까지의 memory 에 올라간 정보](https://3676071105-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LNPupV2z3QokH-IFxbX%2F-LS8CWLXyxYci63J5KOo%2F-LS8NaaEz2MBPxF9kBs-%2Fimage.png?alt=media\&token=b861ef22-26e4-4e01-9493-3a3a83c9940a)

어떻게하면 위의 문제를 해결할 수 있을까요 ?? 바로 프로토타입을 이용하는 것 입니다.\
**Person 의 prototype property 를 이용하면 해결할 수 있습니다.**\
\
Person 자체의 프로토타입 영역에 getName 을 가지고있게 된다면 Person 을 통해 생성되는 객체들은 Person 의 **prototype property** 에 연결되어있기 때문에 가지고 있기 때문에 각 객체별로 따로 getName을 생성하지않고 Person의 프로토타입에 **접근**하여 getNaem의 사용이 가능하게됩니다.

\
결과적으로 **한번만 메모리**에 올리고 가져다 사용할 수 있는 구조로 변경되는 것 입니다.

### 문제 해결을 위해 프로토타입 적용해보기&#x20;

Person 에 프로토타입을 추가해보겠습니다.

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

Person.prototype.getName = function() { // Person 의 프로토타입에 getName 을 추가합니다.
    console.log(this.name);
    return this.name;
}

var person1 = new Person('짱구')
person1.getName(); // 짱
```

getName을 Person의 프로토타입에 추가하면 아래와 같이 객체가 만들어집니다. name 은 자신의 속성으로 getName 의 경우 Person 의 **prototype property** 영역으로 들어가지게됩니다.

![prototype 을 통해 Person 을 구성](https://3676071105-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LNPupV2z3QokH-IFxbX%2F-LS9ZQyJTSY301luU7N1%2F-LS9QPNPDFmWmJdUkkEj%2Fimage.png?alt=media\&token=581493cb-717f-4776-9c6b-e059ba7ef9fc)

![prototype 을 사용하지 않고 Person 을 구성](https://3676071105-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LNPupV2z3QokH-IFxbX%2F-LS9ZQyJTSY301luU7N1%2F-LS9XDyY_evu5xYZfQ7L%2Fimage.png?alt=media\&token=19960b5c-3faa-4064-b067-59a6e35b6dbf)

person1 에 getName 이 없지만 에러가 나지 않고 동작합니다.

```javascript
var person1 = new Person('짱구')
person1.getName();
```

위의 코드는 다음과 같은 순서로 동작합니다.

1. person1 안에서 getName 을 찾는다.
2. person1 내부에 getName 이 없기 때문에 Person 의 **prototype property** 영역에서 getName 을 찾습니다.
3. 이때도 없다면 프로토타입 링크를 타고 **한단계 더 올라가** prototype 영역에서 getName 을 찾습니다.
4. 최상위인 **object** 의 영역을 뒤져서도 없다면 에러가 나게됩니다.

위의 코드는 person 의 **prototype property** 영역에 getName 이 있기 때문에 2 번에서 이름을 return 할 수 있었던 것 입니다.

연결된 프로토타입들을 타고 타고 올라가기 때문에 이러한 동작을 **프로토타입 체인**이라고 부릅니다.

### 함수 체이닝

```javascript
$(selector).css().animation() ....
```

제이쿼리를 사용하시다보면 위의 코드와 같이  . 으로 이어서 함수를 실행할 수 있었습니다. \
어떻게 이런것이 가능한지 알아보기위해 체이닝이라는 개념을 알아보겠습니다.

Cook 이라는 생성자함수는 재료를 받아 여러단계를 거쳐 조리를 해주는 친구입니다.

```javascript
function Cook(ingredients) {
  this.ingredients = ingredients
}

Cook.prototype.dough = function() {
  console.log(`${this.ingredients} 를 반죽합니다.`)
}

Cook.prototype.ferment = function() {
  console.log(`${this.ingredients} 를 발표시킵니다.`)
}

Cook.prototype.bake = function() {
  console.log(`${this.ingredients} 를 굽습니다.`)
}

Cook.prototype.finish = function() {
  console.log(`${this.ingredients} 이 완성되었습니다.`)
}

var baker = new Cook('밀가루')
baker.dough()
baker.ferment()
baker.bake()
baker.finish()
```

요 함수를 아래와 같이 실행하도록 변경하고 싶습니다.

```javascript
new Cook('밀가루').dough().ferment().bake().finish()
```

방법은 간단합니다. 자기 자신을 return 해주면 됩니다.

```javascript
function Cook(ingredients) {
  this.ingredients = ingredients
}

Cook.prototype.dough = function() {
  console.log(`${this.ingredients} 를 반죽합니다.`)
  return this
}

Cook.prototype.ferment = function() {
  console.log(`${this.ingredients} 를 발표시킵니다.`)
  return this
}

Cook.prototype.bake = function() {
  console.log(`${this.ingredients} 를 굽습니다.`)
  return this
}

Cook.prototype.finish = function() {
  console.log(`${this.ingredients} 이 완성되었습니다.`)
  return this
}

new Cook('밀가루')
  .dough()
  .ferment()
  .bake()
  .finish()

```

### 마무리&#x20;

프로토타입을 잘 이용하게된다면 메모리도 아낄 수 있고 코드의 모듈화 또한 가능합니다.

> _proto\__ 안에 보이는 **constructor** 는 prototype property 와 마찬가지고 **자기자신을 생성한 함수**라고 알아두시면 될 것 같습니다. 자세한 내용은 **context** 를 다루면서 작성할 예정입니다.

> 실습 ) Stack 자료구조 만들어보기

풀이 : [https://jsfiddle.net/fakmhLj6/1/](https://jsfiddle.net/fakmhLj6/1/)

> 실습 ) 5 함수에서 만들었던 Slide 를 prototype 으로 변경해보기
