# 프로토 타입
## 프로토타입이란?
- 프로토타입이란 디자인 패턴 중 하나로서 객체를 생성할 때 원본이 되는 객체를 복사해서 생성하는 패턴
- 프로토타입 패턴은 객체를 효율적으로 생성하는 방법을 다루는 패턴 중 하나이고 주로 객체를 생성하는 비용이 클 때 이를 회피하기 위해 사용됨
- 자바스크립트는 ES6 부터 class 키워드를 사용하여 클래스를 지원하고 있지만 정확히 말하면 프로토타입으로 클래스를 흉내내서 구현한 것임
## 자바스크립트에서 프로토타입(\_\_proto\_\_, constructor)
- 클래스 기반 언어에서는 클래스를 생성하고 그 클래스를 사용하여 객체를 생성함. 하지만 자바스크립트는 클래스라는 개념 자체가 없으며 함수(Function)을 사용해서 객체를 생성함
- 자바스크립트는 객체를 생성할 때 프로토타입 패턴을 사용하고 함수의 프로토타입 객체를 복제하여 객체를 생성함
- 자바스크립트는 함수가 생성될 때 자동으로 그 함수의 프로토타입 객체(Prototype Object)를 함께 생성하고 해당 함수의 prototype 프로퍼티에 연결한다.

### constructor
함수가 생성되며 함께 생성된 프로토타입 객체는 모두 constructor라는 프로퍼티를 가지고 있다. 그리고 이 프로퍼티에는 이 프로토타입 객체가 생성될 때 선언했던 함수가 들어있다.

#### constructor 프로퍼티의 존재 이유
이 생성자 프로퍼티는 이 함수를 통해 생성된 객체 입장에서 보았을 때 나를 만들 때 어떤 함수가 호출되었냐?를 의미
프로토타입 객체의 constuctor 프로퍼티에 생성자 함수가 연결되어있기 때문에 새롭게 생성된 객체는 자신의 프로토타입 객체에 접근해 이 프로퍼티를 참조함으로써 자신이 만들어질 때 어떤 생성자 함수가 호출되었는지를 알 수 있다.(instanceof 연산자를 통해서 확인할 수 있다.)
### \_\_proto\_\_
> __proto__는 ECMAScript 2015에서는 표준이었지만 현재는 표준이 아니므로 Object.getPrototypeOf(object), Object.setPrototypeOf(object, prototype)를 사용하는 것을 추천
함수의 프로토타입 객체를 복제하여 생성된 객체가 자신의 원본(프로토타입) 객체에 접근할 수 있는 프로퍼티가 바로 \_\_proto\_\_ 이다.
새롭게 생성된 객체와 프로토타입 객체와의 연결을 **프로토타입 링크(Prototype Link)** 라고 한다.
## 프로토타입 체인
프로토타입 링크로 이루어진 객체들의 관계를 프로토타입 체인(Prototype Chain)이라고 한다.
자바스크립트 내에 존재하는 모든 것들은 Object 함수의 프로토타입인 Object.prototype을 시작으로 해서 복제된다. 그렇기 때문에
Object.prototype 객체는 프로토타입 링크, 즉 프로토타입 객체로 통하는 링크가 없다.
자바스크립트의 모든 함수는 원본으로 Function.prototype 객체를 가진다. 그리고 Function.prototype은 결국 객체이기 때문에, 당연히 원본으로 Object.prototype 객체를 가진다.

### 프로토타입 룩업
프로토타입 체인을 통해 객체의 메서드나 프로퍼티를 찾아가는 과정을 프로토타입 룩업이라고 한다.
```javascript=
var a = {
attr1: 'a'
};
var b = {
__proto__: a,
attr2: 'b'
};
var c = {
__proto__: b,
attr3: 'c'
};
// 위의 코드에서는 객체를 세 개 만들고
// 각 객체의 __proto__ 속성을 이용해 c -> b-> a 로 연결했다.
c.attr1 // 'a'
```
현재 객체에서 원하는 프로퍼티를 찾지못하는 경우 객체의 \_\_proto\_\_ 프로퍼티가 참조하는 객체에서 값을 찾는다. 최종적으로 \_\_proto\_\_가 없는 Object 객체까지 올라가고 못 찾은 경우 undefined를 출력한다.
## 프로토타입을 활용한 상속
자바스크립트에서는 `Object.create` 메소드를 사용하여 상속을 구현한다.
### Object.create()
```javascript
Object.create(proto, [propertiesObject])
```
#### 정의
지정된 프로토타입 객체 및 속성(property)을 갖는 빈 객체를 만든다.
여기서 __proto__는 인자로 받은 proto 객체를 가리킨다.
이때 프로퍼티 설명자를 추가로 넘길 수 있다.
### 상속 예제
```javascript=
function SuperClass (name) {
this.name = name;
}
SuperClass.prototype.say = function () {
console.log(`I am ${this.name}`);
}
function SubClass (name) {
SuperClass.call(this, name);
}
SubClass.prototype = Object.create(SuperClass.prototype);
SubClass.prototype.constructor = SubClass;
SubClass.prototype.run = function () {
console.log(`${this.name} is running`);
}
const subObj = new SubClass('john');
```
#### 1.

```javascript
function SuperClass (name) {
this.name = name;
}
SuperClass.prototype.say = function () {
console.log(`I am ${this.name}`);
}
```
SuperClass 함수와 prototype 객체가 만들어지고 prototype의 say 함수를 초기화해주는 코드이다.
#### 2.

```javascript
function SubClass (name) {
SuperClass.call(this, name);
}
```
SubClass 함수와 prototype 객체가 만들어진 코드이다. 아직까지 SubClass 와 SuperClass와의 연결점은 없다.
※ SubClass에서 call 함수를 호출하는 이유
- 부모클래스의 멤버 변수, 함수들을 자식 클래스에서 초기화하기 위해서이다. JAVA의 super()와 유사한 역할이다.
#### 3.

```javascript
SubClass.prototype = Object.create(SuperClass.prototype);
```
SubClass의 prototype를 `Object.create`를 통해 복제된 SuperClass의 prototype으로 변경한다. 이 과정에서 복제된 prototype의 \_\_proto\_\_ 프로퍼티는 SuperClass의 prototype을 가리키게 된다.
#### 4.

```javascript
SubClass.prototype.constructor = SubClass;
```
`Object.create()`을 사용할 경우, 새로 만들어진 객체에는 constructor가 존재하지 않는다. 따라서 SubClass prototype 객체의 constructor를 SubClass 함수로 변경한다.
#### 5.

```javascript
SubClass.prototype.run = function () {
console.log(`${this.name} is running`);
}
```
상속된 SubClass는 \_\_proto\_\_ 프로퍼티를 통해서 부모와 연결되어 있다.
자식 객체에서 변수, 함수를 추가해도 부모에는 영향을 미치지 않는다.
#### 6.

```javascript
const subObj = new SubClass('john');
```
subObj -> SubClass.prototype -> SuperClass.prototype으로 이어지는 프로토타입 체인이 완성된 것을 확인할 수 있다. 이때 subObj 객체의 run이나 say 메소드를 호출하면 위에서 언급한 프로토타입 룩업을 통해 프로토타입 객체의 메소드를 호출할 수 있다.