# 프로토 타입 ## 프로토타입이란? - 프로토타입이란 디자인 패턴 중 하나로서 객체를 생성할 때 원본이 되는 객체를 복사해서 생성하는 패턴 - 프로토타입 패턴은 객체를 효율적으로 생성하는 방법을 다루는 패턴 중 하나이고 주로 객체를 생성하는 비용이 클 때 이를 회피하기 위해 사용됨 - 자바스크립트는 ES6 부터 class 키워드를 사용하여 클래스를 지원하고 있지만 정확히 말하면 프로토타입으로 클래스를 흉내내서 구현한 것임 ## 자바스크립트에서 프로토타입(\_\_proto\_\_, constructor) - 클래스 기반 언어에서는 클래스를 생성하고 그 클래스를 사용하여 객체를 생성함. 하지만 자바스크립트는 클래스라는 개념 자체가 없으며 함수(Function)을 사용해서 객체를 생성함 - 자바스크립트는 객체를 생성할 때 프로토타입 패턴을 사용하고 함수의 프로토타입 객체를 복제하여 객체를 생성함 - 자바스크립트는 함수가 생성될 때 자동으로 그 함수의 프로토타입 객체(Prototype Object)를 함께 생성하고 해당 함수의 prototype 프로퍼티에 연결한다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/1469bceee17e60893b8f93b2f92e788b329aae48/SS%25EC%2584%25A0%25ED%2583%259D%2520%25EC%2598%2581%25EC%2597%25AD_154.png) ### constructor 함수가 생성되며 함께 생성된 프로토타입 객체는 모두 constructor라는 프로퍼티를 가지고 있다. 그리고 이 프로퍼티에는 이 프로토타입 객체가 생성될 때 선언했던 함수가 들어있다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/1469bceee17e60893b8f93b2f92e788b329aae48/SS%25EC%2584%25A0%25ED%2583%259D%2520%25EC%2598%2581%25EC%2597%25AD_155.png) #### 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 객체를 가진다. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/288d1e5871cb692263cabce53d7d85cbb9fb0f8c/%25EC%2584%25A0%25ED%2583%259D%2520%25EC%2598%2581%25EC%2597%25AD_156.png) ### 프로토타입 룩업 프로토타입 체인을 통해 객체의 메서드나 프로퍼티를 찾아가는 과정을 프로토타입 룩업이라고 한다. ```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. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/c910c363b93f9432f35310c55abd87830b35ba81/Slide112.jpg) ```javascript function SuperClass (name) { this.name = name; } SuperClass.prototype.say = function () { console.log(`I am ${this.name}`); } ``` SuperClass 함수와 prototype 객체가 만들어지고 prototype의 say 함수를 초기화해주는 코드이다. #### 2. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/24020adf1a111e1ec7667ccd3da4bb9ad3e60280/Slide113.jpg) ```javascript function SubClass (name) { SuperClass.call(this, name); } ``` SubClass 함수와 prototype 객체가 만들어진 코드이다. 아직까지 SubClass 와 SuperClass와의 연결점은 없다. ※ SubClass에서 call 함수를 호출하는 이유 - 부모클래스의 멤버 변수, 함수들을 자식 클래스에서 초기화하기 위해서이다. JAVA의 super()와 유사한 역할이다. #### 3. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/0055deac52ba11ef0733fa6a78fa59c61a93f50a/Slide114.jpg) ```javascript SubClass.prototype = Object.create(SuperClass.prototype); ``` SubClass의 prototype를 `Object.create`를 통해 복제된 SuperClass의 prototype으로 변경한다. 이 과정에서 복제된 prototype의 \_\_proto\_\_ 프로퍼티는 SuperClass의 prototype을 가리키게 된다. #### 4. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/0055deac52ba11ef0733fa6a78fa59c61a93f50a/Slide115.jpg) ```javascript SubClass.prototype.constructor = SubClass; ``` `Object.create()`을 사용할 경우, 새로 만들어진 객체에는 constructor가 존재하지 않는다. 따라서 SubClass prototype 객체의 constructor를 SubClass 함수로 변경한다. #### 5. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/0055deac52ba11ef0733fa6a78fa59c61a93f50a/Slide116.jpg) ```javascript SubClass.prototype.run = function () { console.log(`${this.name} is running`); } ``` 상속된 SubClass는 \_\_proto\_\_ 프로퍼티를 통해서 부모와 연결되어 있다. 자식 객체에서 변수, 함수를 추가해도 부모에는 영향을 미치지 않는다. #### 6. ![image](https://gist.githubusercontent.com/ji3427/308e58e69c5f5385af26efe704813d6d/raw/c6cf30e8324d64b55329dc2f1b199d3bcc17486b/image_2021_01_22T08_24_52_169Z.png) ```javascript const subObj = new SubClass('john'); ``` subObj -> SubClass.prototype -> SuperClass.prototype으로 이어지는 프로토타입 체인이 완성된 것을 확인할 수 있다. 이때 subObj 객체의 run이나 say 메소드를 호출하면 위에서 언급한 프로토타입 룩업을 통해 프로토타입 객체의 메소드를 호출할 수 있다.