Prototype 패턴은 동일 타입의 여러 객체들이 프로퍼티를 공유할 때 유용하게 사용한다.
Prototype은 Javascript 객체의 기본 속성이므로 Prototype 체인을 활용할 수 있다.
하나의 앱을 만들 때 동일한 타입의 여러 객체를 만들어내곤 한다. ES6 클래스의 여러 인스턴스를 만들어낼 때 유용하게 사용할 수 있다.
여러 강아지 클래스를 만들어보자. 예제에서 강아지는 이름을 가지고 있고 짖을 수 있다.
class Dog {
constructor(name) {
this.name = name
}
bark() {
return 'Woof!'
}
}
const dog1 = new Dog('Daisy');
const dog2 = new Dog('Max');
const dog3 = new Dog('Spot');
Dog
클래스의 생성자에서는 name
프로퍼티를 가지고 있고 클래스 자체적으로는 bark
프로퍼티를 가지고 있다.
ES6 클래스를 사용하면 모든 프로퍼티는 클래스 자체에 선언되며 위의 코드에서 bark
는 자동으로 prototype
에 추가된다.
생성자의 prototype
프로퍼티 혹은 생성된 인스턴스의 __proto__
프로퍼티를 통해 Prototype 객체를 확인할 수 있다.
__proto__
는 비표준이므로__proto__
대신Object.getPrototypeOf
을 사용하는 것이 좋다.
console.log(Dog.prototype)
// constructor: f Dog(name) bark: f bark()
console.log(dog1.__proto__)
// constructor: f Dog(name) bark: f bark()
어떤 인스턴스던지 __proto__
의 값은 Prototype 객체를 가리킨다. 객체에 없는 프로퍼티에 접근하려 하는 경우 Javascript는 이 프로퍼티가 나타날 때까지 prototype chain을 거슬러 올라간다.
Prototype 패턴은 객체들이 같은 프로퍼티를 가져야 하는 경우 유용하게 쓰일 수 있다. 중복된 프로퍼티들이 존재하는 객체를 매번 생성하기 보다, Prototype에 프로퍼티를 추가하면 모든 인스턴스들이 Prototype 객체를 활용할 수 있다.
모든 인스턴스들이 Prototype에 접근할 수 있기 때문에 인스턴스를 만든 뒤에도 Prototype에 프로퍼티를 추가할 수 있다.
예제에서 강아지는 짖기만 가능했지만 놀 수도 있게 구현해보자. Prototype 객체에 play
프로퍼티를 추가하여 가능하다.
class Dog {
constructor(name) {
this.name = name
}
bark() {
return 'Woof!'
}
}
const dog1 = new Dog('Daisy');
const dog2 = new Dog('Max');
const dog3 = new Dog('Spot');
Dog.prototype.play = () => console.log("Playing now!");
dog1.play();
Prototype "체인"이란 단어처럼 Prototype은 한 단계 이상도 존재할 수 있다. 지금까지 어떤 인스턴스의 proto 속성에 대해서 이야기했지만, 사실 Prototype 객체 자체도 proto 속성을 가질 수 있다.
이제 다른 타입의 강아지를 만들어 보자. 이 강아지는 Dog
의 속성을 모두 가지고 있지만 하늘을 날 수 있다. 이 슈퍼 강아지는 Dog
클래스를 상속받아 fly
메서드를 구현하면 된다.
class SuperDog extends Dog {
constructor(name) {
super(name)
}
fly() {
return 'Flying!'
}
}
아래 예제에서는 Daisy
라는 강아지를 만들고 짖거나 하늘을 날 수 있게 하고 있다.
class Dog {
constructor(name) {
this.name = name
}
bark() {
console.log('Woof!')
}
}
class SuperDog extends Dog {
constructor(name) {
super(name)
}
fly() {
console.log('Flying!')
}
}
const dog1 = new SuperDog("Daisy");
dog1.bark();
dog1.fly();
SuperDog
는 Dog
를 상속했다. 따라서 인스턴스 dog1
은 bark 메서드도 호출할 수 있다.
SuperDog
의 Prototype 객체의 proto는 Dog.prototype
을 가리키고 있다.
Prototype 체인이라고 불리는 이유가 명확해졌다. 현재 객체에 없는 프로퍼티에 접근하려 하는 경우 Javascript는 같은 이름의 프로퍼티를 찾을 때까지 재귀적으로 객체의 proto를 따라 거슬러 올라가게 된다.
Object.create
메서드는 Prototype으로 쓰일 객체를 인자로 받아 새로운 객체를 만들어낸다.
const dog = {
bark() {
return 'Woof!'
}
}
const pet1 = Object.create(dog)
pet1
자체적으로는 아무런 프로퍼티도 없지만, dog
객체를 Prototype으로 사용하기 때문에 bark 메서드를 사용할 수 있다.
const dog = {
bark() {
console.log(`Woof!`);
}
};
const pet1 = Object.create(dog);
pet1.bark(); // Woof!
console.log("Direct properties on pet1: ", Object.keys(pet1));
console.log("Properties on pet1's prototype: ", Object.keys(pet1.__proto__));
Object.create
는 단순히 객체가 다른 객체로부터 프로퍼티를 상속받을 수 있게 해준다. 실행 결과로 생성되는 객체는 Prototype 체인으로 인해 인자로 넘어갔던 객체의 프로퍼티를 활용할 수 있는 것이다.
Prototype 패턴은 어떤 객체가 다른 객체의 프로퍼티를 상속받을 수 있도록 해준다. Prototype 체인을 통해 해당 객체에 프로퍼티가 직접 선언되어 있지 않아도 되므로 메서드 중복을 줄일 수 있고 이는 메모리 절약으로 이어진다.
< 출처 : https://patterns-dev-kr.github.io/design-patterns/prototype-pattern/ >