본문 바로가기

웹 개발

디자인 패턴) Prototype Pattern

이 글을 공부하면서 번역하고 정리한 글입니다. 


같은 타입의 여러 객체들 간 속성을 공유하는 패턴

prototype 패턴은 같은 타입의 여러 객체 간 속성을 공유하기 좋습니다. 프로토타입은 JavaScript의 native 객체이며 프로토타입 체인을 통해 객체에 접근할 수 있습니다. 

우리는 하나의 애플리케이션에서 같은 타입의 객체를 많이 만들어 사용합니다. 이때, ES6의 클래스의 인스턴스를 여러개 만들어 사용합니다. 

Example

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");

ES6 클래스를 사용하면 클래스에 정의된 모든 속성들은 위 예제에서는 'bark'라는 함수는 자동으로 프로토타입에 추가됩니다. 

console.log(Dog.prototype);
// constructor: ƒ Dog(name, breed) bark: ƒ bark()
 
console.log(dog1.__proto__);
// constructor: ƒ Dog(name, breed) bark: ƒ bark()

 

생성자의 prototype이나 클래스의 인스턴스의 __proto__를 통해 프로토타입을 직접 확인할 수 있습니다. 클래스 인스턴스의 __proto__ 값은 생성자의 프로토타입을 직접 참조합니다! 객체에 존재하지 않는 속성에 직접 접근하려고 하면 JavaScript는 프로토타입 체인을 타고 내려가 해당 속성이 프로토타입 체인 내에서 존재하는 것인지 확인합니다.

출처ㅣ https://www.patterns.dev/posts/prototype-pattern

 

프로토타입 패턴은 같은 속성에 접근해야 하는 객체들을 다룰 때 유용합니다. 모든 인스턴스들이 하나의 프로토타입 객체에 접근할 수 있기 때문에 매번 속성을 복제하는 대신, 프로토타입에 속성을 추가하면 됩니다. 인스턴스를 이미 생성한 이후에도 속성을 추가하기가 쉽습니다. 

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();

이와 같이 Dog.prototype에 play라는 속성을 추가할 수 있습니다. 

프로토타입 체인이라는 말은 한 단계 이상이 존재할 수 있다는 것을 의미합니. 위에서 인스턴스의 __proto__ 값으로 접근할 수 있는 속성들을 살펴보습니다. 하지만 프로토타입은 프로토타입 자체도 __proto__ 객체를 가지고 있습니다. 

또 다른 예제로 SuperDog라는 클래스를 만들어봅시다! 이것은 위에서 만든 Dog라는 클래스를 상속하면서 fly라는 속성이 추가됩니다. 

class SuperDog extends Dog {
  constructor(name) {
    super(name);
  }
 
  fly() {
    return "Flying!";
  }
}

Daisy라는 SuperDog 인스턴스를 만들어서 Dasiy가 bark하거나 fly하게 해봅시다!

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 클래스를 상속했기 때문에 bark 메소드에 접근할 수 있습니다. 그리고 SupoerDog의 프로토타입의 __proto__ 값은 Dog.prototype 객체를 참조합니다. 

이제 왜 포로토타입 체인이라고 부르는지 이해가 되죠? 객체에 직접 존재하지 않는 속성에 접근하려 할 때 JavaScriptsms 객체의 __proto__ 값이 가리치는 객체들을 타고 올라가면서 해당 속성을 찾는 것입니다.


Object.create

Object.create 메소드는 프로토타입에 값을 직접 전달하여 새로운 객체를 만드는데 사용합니다. 

const dog = {
  bark() {
    return `Woof!`;
  },
};
 
const pet1 = Object.create(dog);

pet1은 아무런 속성도 가지고 있지 않지만, 프로토타입 체인에 있는 속성들에 접근할 수 있습니다. pet1의 프로토타입에 dog 객체를 전달했기 때문에 pet1은 bark 메소드에 접근할 수 있습니다. 

const dog = {
  bark() {
    console.log(`Woof!`);
  }
};

const pet1 = Object.create(dog);

pet1.bark(); // Woof!
console.log("Direct properties on pet1: ", Object.keys(pet1)); // Direct properties on pet1:  
[]
console.log("Properties on pet1's prototype: ", Object.keys(pet1.__proto__)); // Properties on pet1's prototype:  
(1) ["bark"]

Object.create는 새로 생성할 객체의 프로토타입을 직접 명시하여 어떤 객체에서 다른 객체로 속성을 직접 상속하는 간단한 방법입니다. 그렇게 생성된 객체는 프로토타입 체인을 타고 새로운 속성에 접근할 수 있습니다. 


프로토타입 패턴은 객체가 다른 객체의 속성에 접근하거나 상속하기 쉽게 만들어줍니다. 프로토타입 체인은 객체 자체에 직접 정의되지 않은 속성에 접근할 수 있게 만들기 때문에 메소드나 속성을 중복해서 정의하지 않게 해주고 이로 인해 메모리 사용을 감소하게 해줍니다. 

'웹 개발' 카테고리의 다른 글

디자인 패턴) Container/Presentational Pattern  (0) 2023.07.17
디자인 패턴) Provider Pattern  (0) 2023.07.12