자바스크립트 Prototype과 Instance
FE BE 개발 메모장/Javascript

자바스크립트 Prototype과 Instance

프로토타입 객체

프로토타입(Prototype)의 등장

 일반적으로 객체를 만들어서 해당 객체를 복사하여 사용할 경우, 객체에 들어있는 프로퍼티와 함수가 복사한 객체 개수만큼 생성이 되는 매우 비효율적인 상황이 발생한다. 그래서 이 문제를 해결하기 나온 것이 프로토타입(Prototype)이다.

자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어이다.

 클래스 기반의 객체지향 프로그래밍 언어에선 먼저 클래스를 정의하고, 인스턴스라는 객체를 생성했다. 프로토타입 기반의 객체지향 언어는 클래스(class)없이 객체를 생성이 가능하며, 자바스크립트가 해당했다.(ES6에서 class가 추가됨)

 

자바스크립트 Prototype

  • 생성자 함수로 생성한 객체들이 프로퍼티와 메소드를 공유하기 위해 사용하는 객체이다.
  • 함수만 가지고 있는 프로퍼티이고, 자바스크립트는 모든 것이 객체이므로 함수도 프로퍼티를 가질 수 있다. 
  • Prototype는 자바스크립트에서 자동으로 만들어준다.

pseudoclassical 방식

//함수를 만드는 것과 비슷하지만 함수명은 대문자로 시작하는 규칙이다.
function Player1(name){
    this.name = name; 
}

//methods를 추가
Player1.prototype.run = function() {
  console.log(this.name + "가(이) 탈주했습니다.")
}

function Player2(name) {
  //call을 쓸 경우
  Player1.call(this, name);
  //apply를 쓸 경우
  Player1.apply(this, [name]); //인자가 여러개인 경우 [...args] (spread Syntax를 쓸수있다)
}

//Object.create로 Player1의 프로토타입의 프로토타입을 복제하여 Player2.prototype에 담아준다.
//일종의 shallow copy로 볼 수 있다.
Player2.prototype = Object.create(Player1.prototype);
//생성자를 Player2로 이어준다.
Player2.prototype.constructor = Player2;

Player2.prototype.report = function() { 
  console.log(`${this.name}님이 ${user1.name}을 신고했습니다.`)
}

const user1 = new Player('탈주 장인');
const user2 = new Player2('브실골탈출')

ES6

//ES-6에선 복잡한 과정이 대부분 생략되었다.
class Player1{
  constructor(name){
    this.name = name;
   }
    run() {
     console.log(this.name + "가(이) 탈주했습니다.")
   }
}

class Player2 extends Player1{
 constructor(name) {
     super(name);
}
  report(){
    console.log(`${this.name}님이 ${user1.name}을 신고했습니다.`)
  }
}

const user1 = new Player('탈주 장인');
const user2 = new Player2('브실골탈출')

생성자함수 프로퍼티(Constructor property)

constructor는 인스턴스가 초기화 될 때 실행해주는 생성자 함수이며, 함수 자신을 가르킨다. 새로운 클래스를 정의할 때, 단, 하나의 constructor을 반드시 넣어주는 규칙을 가진다.

 

function Alphabet(){}
//Alphabet.prototype = { constructor: Alphabet }

let newLang = new Alphabet(); // { constructor: Alphabet }을 상속받음

console.log(newLang.constructor === Alphabet) // true; [[Prototype]]을 거쳐서 접근한다.

constructor 프로퍼티를 사용하면 기존에 있던 객체에서 새로운 객체를 만들 수 있다.

 

//Object.create로 Player1의 프로토타입의 프로토타입을 복제하여 Player2.prototype에 담아준다.
//일종의 shallow copy로 볼 수 있다.
Player2.prototype = Object.create(Player1.prototype);
//생성자를 Player2로 이어준다.
Player2.prototype.constructor = Player2;

ES-6에서 Class가 추가되면서 아래와 같이 쓰인다.

 

class Player1{
  constructor(name){
    this.name = name;
   }
  
class Player2 extends Player1{
 constructor(name) {
     super(name);
}

.__proto__( [[prototype]] )

함수(Function)를 포함한 모든 객체가 가지고 있으며, 현재 객체에서 자신의 부모 역할인 프로토타입 객체를 가르킨다. ECMAScript에서는 암묵적 프로토타입 링크라고 부르며 __proto__프로퍼티에 저장된다.

 

__proto__는 객체를 만들 때 생성자(constructor)의 Prototype이 참조된 모습이다. 

 

위 이미지에서와 같이 

인스턴스 user1 [[Prototype]] new 연산자를 통해 prototype 객체의 메소드를 할당받는다. 결국 prototype 객체와 user1.__proto__는 일치한다.

constructor 생성자 함수는 자신을 생성한 부모 함수 혹은 클래스인 Player1을 가르키고있다. 

 

 user1의 조상을 확인하기위해 .__proto__로 보게되면 constructor는 class로 선언된 Player1을 가르키고, user1.__proto__.__proto__는 최상위 객체인 Object를 가르키고있다.

 

그러면 Player2의 족보는 무엇일까?

 

user2.__proto__와 프로토타입 객체(Player2.prototype)는 Player2를 가르킴과 동시에 Player1을 보여주고있다. 그렇다면 Player1과 같다고 생각을 했었는데. 직접 콘솔을 돌려보니 일치하지 않았다. 그 이유를 알아보니 코드에 나와있듯이 '참조'를 하기 때문이다.

 

pseudoclassical 방식

//Object.create로 Player1의 프로토타입의 프로토타입을 복제하여 Player2.prototype에 담아준다.
//일종의 shallow copy로 볼 수 있다.
Player2.prototype = Object.create(Player1.prototype);
//생성자를 Player2로 이어준다.
Player2.prototype.constructor = Player2;

ES6 방식

class Player2 extends Player1{
 constructor(name) {
     super(name);
}

 위 코드에서 보았듯이 extends와 Object.create(프로토타입 객체)는 첫번째 인자로 들어가는 프로토타입 객체를 기반으로 프로토타입을 만든다 라고 되있지만, 이해하기 어려워 이렇게 생각하기로 했다. Player1.prototype를 복제하여 새로운 프로토타입 객체를 만들어 내는 행위라고 해석했다. 이 과정을 얕은복사(shallow copy)와 유사하다고 생각되었다.

 

Prototype Chain

자바스크립트는 특정 객체의 프로퍼티나 메소드에 접근하려고 할 때, 해당 객체에 접근하려는 프로퍼티나 메소드가 없다면 [[Prototype]]이 가리키는 경로를 따라서, 자신의 부모역활이 되는 객체의 프로퍼티나, 메소드를 찾는다. 

 

예시)

let keyboard = {
    type: 'Mechanical'
    switch: 'Cherry MX Brown'
    model: 'leopold 750'
}

console.log(keyboard.hasOwnProperty('type')); // true

keyboard 객체는 hasOwnProperty 메소드를 갖고 있지 않아 에러가 발생해야 하지만, 정상적으로 출력되었다. 이것은 [[Prototype]]이 가리키는 경로를 따라서 keyboard객체의 부모 역할을 하는 Object.prototype의 메소드를 호출하였기 때문에 가능한 것이다.

 

결국, 모든 프로토타입 체이닝의 종점은 Object.prototype이다.

 

ko.javascript.info/function-prototype#ref-285

mygumi.tistory.com/312

모든 글 원문 - poiemaweb.com/js-prototypepoiemaweb.com/js-prototype

iamsjy17.github.io/javascript/2019/06/10/js33_17_prototype.html

doitnow-man.tistory.com/132

'FE BE 개발 메모장 > Javascript' 카테고리의 다른 글

Promise  (0) 2021.02.01
비동기 처리, 콜백함수  (0) 2021.02.01
객체(Object)와 객체를 생성하는 방법.  (0) 2021.01.14
함수 메소드(call, apply, bind)  (0) 2021.01.14
window 객체와 this  (0) 2021.01.12