Home Javascript prototype
Post
Cancel

Javascript prototype

들어가며

  • 이 내용은 코어 자바스크립트의 강의 중 prototype과 class를 정리한 내용 입니다.
  • 강의링크

프로토타입

  • 상속되는 속성과 메소드들이 정의되어 있는 곳의 속성을 의미1
  • 생성자 함수와 new2 키워드를 이용해서 instance를 생성하면 Constructor3prototype이라고 하는 프로퍼티의 내용이 instance의 [[Prototype]]라고 하는 프로퍼티로 참조를 전달하게 된다.

리터럴의 경우

  • 리터럴의 경우에는 instance가 아니므로 [[Prototype]]이 존재하지 않는다.
  • 하지만 메서드 처럼 접근이 가능한데, 가능한 이유는 javascript에서 타입에 해당되는 객체의 인스턴트를 임시로 만들어서 메서드를 호출하기 때문에 가능하다.
  • 메서드 호출이 끝나고 임시로 만든 instance는 삭제 된다.

프로토타입 체이닝

  • Constructor의 prototype은 Object 생성자 함수의 new 연산으로 생성된 instance이다.
  • instance는 모두 [[Prototype]]을 가지고 있으므로 Constructor의 prototype[[Prototype]]은 Object의 prototype이 된다.
  • 따라서 Object의 prototype 도 접근할 수 있게 된다.
  • 그러므로 Object 전용 메서드를 Object의 prototype에 정의하면 안된다. 왜냐면 다른 객체에서 호출 할 수 있기 때문이다.
  • 따라서 유독 Object.xxxx(obj); 형태의 메서드가 많은 이유가 프로토타입 체이닝 특성 때문이다.

예시

  • Ararry에는 toString이라는 메서드가 있다. 그래서 배열의 toString을 호출하면 아래와 같이 나오는데, 이는 Array 객체의 prototype에 존재하는 toString 메서드를 호출 하기 때문이다.

    1
    2
    
    const arr = [1,2,3];
    arr.toString(); // "1,2,3"
    
  • 만약 Array 객체의 prototype에 존재하는 toString를 지우면 다음과 같이 나올 것이다.

    1
    2
    3
    
    const arr = [1,2,3];
    delete Array.prototype.toString;
    arr.toString(); // "[object Array]"
    
  • 이유는 이제 Array 객체의 prototype에는 toString 메서드가 존재 하지 않기 때문에, 프로토타입 체이닝을 통해서 Object의 prototype에 존재하는 toString 메서드를 호출 하기 때문이다. 이것도 스코프 체인과 비슷하게 근접한 곳에서 부터 탐색한다.

프로토타입을 응용한 class 구현

ES6 전에는 javascript에 class라는 키워드가 존재하지 않았다. 따라서 아래와 같이 구현한다.

기본적인 구현 방법

  1. 부모, 자식 객체의 생성자 함수를 만든다. (Food, Apple)
  2. 부모 객체에서 자식 객체에게 상속시킬 메서드를 prototype에 정의 한다.
  3. 자식의 prototype을 부모의 새로운 instance로 대체한다.
  4. 자식의 prototypeconstructor는 부모의 객체로 대체 되었기 때문에 다시 자식의 객체로 변경한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Food(color){
    this.color = color;
}

function Apple(color){
    this.color = color;
}

// 부모 객체에서 자식 객체에게 상속시킬 메서드를 prototype에 정의 한다.
Food.prototype.printFoodColor = function() {
    console.log(`Color:`, this.color);
}

// 자식의 prototype을 부모의 새로운 instance로 대체한다.
Apple.prototype = new Food();

// 자식의 prototype의 constructor는 부모의 객체로 대체 되었기 때문에 다시 자식의 객체로 변경한다.
Apple.prototype.constructor = Apple;

const apple = new Apple('green');
apple.printFoodColor(); // Color: green

기본적인 개념은 다음과 같은데, 여기서 보완해야될 부분이 있다.

  • Food의 color가 Apple의 prototype에 남게 된다.
  • Apple에도 color를 지정하고 Food에서도 color를 지정하는 코드 중복이 일어난다.

보완한 구현 방법 1 - 중복 프로퍼티 제거

  1. Bridge라는 빈 객체를 만든다.
  2. Bridge의 prototype에 부모의 prototype을 대입한다.
  3. Bridge의 새로운 인스턴스를 자식의 prototype에 대입한다.
1
2
3
4
5
6
7
8
9
10
11
12
// Bridge라는 빈 객체를 만든다.
function Bridge(){};

// Bridge의 prototype에 부모의 prototype을 대입한다.
Bridge.prototype = Food.prototype;

// Bridge의 새로운 인스턴스를 자식의 prototype에 대입한다.
Apple.prototype = new Bridge(); 
Apple.prototype.constructor = Apple;
const apple = new Apple('green');
apple.printFoodColor();

이렇게 되면 기존에 Food에서는 생성자를 통해서 color이라는 속성이 생겼지만 Bridge 생성자에서는 아무것도 하지 않으므로 color라는 속성이 생기지 않게되므로, Food의 prototype만을 Apple에게 상속시킬 수 있게 된다.

보완한 구현 방법 2 - 생성자 함수 중복코드 제거

  1. Bridge라는 빈 객체를 만든다.
  2. Bridge의 prototype에 부모의 prototype을 대입한다.
  3. Bridge의 새로운 인스턴스를 자식의 prototype에 대입한다. (여기까지 동일)
  4. 자식의 prototype의 superClass에 부모를 지정한다. (이름은 상관없음)
  5. 자식 생성자 함수에서 superClass를 호출한다. 호출된 superClass는 메서드로써 호출되었기 때문에 thisBind에 의해서 this가 자식의 instance가 되므로 color가 자식의 instance의 프로퍼티가 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function Food(color){
    this.color = color;
}

function Apple(color){
  // 자식 생성자 함수에서 superClass를 호출한다.
    this.superClass(color);
}

// Bridge라는 빈 객체를 만든다.
function Bridge(){};

// Bridge의 prototype에 부모의 prototype을 대입한다.
Bridge.prototype = Food.prototype;

// Bridge의 새로운 인스턴스를 자식의 prototype에 대입한다.
Apple.prototype = new Bridge(); 
Apple.prototype.constructor = Apple;

// 자식의 prototype의 superClass에 부모를 지정한다.
Apple.prototype.superClass = Food; 
const apple = new Apple('green');
apple.printFoodColor();

참고 - 상속관계를 함수화

위와 같은 코드의 구성은 부모 자식 관계를 맺을 때마다 작성하는 귀찮다. 따라서 아래와 같은 함수를 만들어서 쉽게 상속관계를 맺을 수 있도록 하는것을 추천하고 있다고 한다.

1
2
3
4
5
6
7
8
9
10
11
var extendClass = (function() {
    function Bridge(){}
    return function(Parent, Child) {
        Bridge.prototype = Parent.prototype;
        Child.prototype = new Bridge();
        Child.prototype.constructor = Child;
        Child.prototype.superClass = Parent;
    }
})();

extendClass(Food, Apple);
  1. 출처 MDN 링크 

  2. new 연산자 링크 

  3. Constructor: instance의 생성자 함수 (예를들어 배열 instance이면 Array) 

This post is licensed under CC BY 4.0 by the author.