Prototype pattern это удобный способ разделить свойства между многими объектами одного типа. Прототип это нативный объект JavaScript, он доступен объектам через обход по цепочке прототипов.
В наших приложениях нам часто необходимо создать множество объектов одного типа. С помощью ES6 это делается путем создания экземпляров ES6 класса.
Допустим, нам нужно создать много собак. В нашем примере собаки будут иметь имя – name и они смогут лаять – bark:
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");
В нашем конструкторе constructor содержится свойство name, так же класс содержит метод bark. В классах ES6 все свойства, определенные в самом классе, в данном случае bark, автоматически добавляются к прототипу – prototype (данное определение не относится к статичным методам, которые присваиваются самому классу).
Мы можем посмотреть prototype напрямую, обращаясь к prototype свойству конструктора, или к свойству __proto__ экземпляра класса.
console.log(Dog.prototype);
// constructor: ƒ Dog(name, breed) bark: ƒ bark()
console.log(dog1.__proto__);
// constructor: ƒ Dog(name, breed) bark: ƒ bark()
Значение __proto__ любого экземпляра конструктора – это ссылка на прототип этого конструктора! Как только мы пытаемся обратиться к свойству объекта, которое не существует в этом объекте, JavaScript пройдет по цепочке прототипов в поиске данного свойства.

Prototype pattern имеет большой потенциал, когда нужно работать с объектами, имеющими доступ к одинаковым свойствам. Вместо создания дубликата свойства каждый раз, мы можем добавить свойство к прототипа, и все экземпляры получат к нему доступ через своем свойство __proto__.
Так как все экземпляры имеют доступ к прототипу, становится очень легко добавлять новые свойства, даже после того, как экземпляры созданы.
Допустим, наши собаки должны не только лаять, но уметь играть. Мы можем добавить метод 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();
Термин цепочка прототипов или ptototype chain указывает, что это не конечный этап. До сих пор мы увидели как получать доступ к свойствам объекта, напрямую доступного через свойство __proto__. Однако прототипы сами имеют объекты __proto__!
Давайте создадим другой тип собаки – супер собаку! Эта собака наследует поведение от обычной Dog, но еще получает способность летать. Мы можем создать SuperDog, расширяя класс 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();
Мы имеем доступ к методу bark, потому мы расширили класс Dog. Значение __proto__ в прототипе SuperDog ссылается на объект Dog.prototype.

Становится понятно, почему это называется цепочкой прототипов: когда мы пытаемся получить доступ к свойству, которое отсутствует в объекте, JavaScript рекурсивно проходит вниз по цепочке объектов, находящихся в свойстве __proto__, пока не найдет это свойство.
Object.create
Метод Object.create позволяет создать объект, которому мы можем явно передать значение прототипа.
const dog = {
bark() {
return `Woof!`;
}
};
const pet1 = Object.create(dog);
Хотя объект 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));
console.log("Properties on pet1's prototype: ", Object.keys(pet1.__proto__));
Отлично! Object.create – это простой способ позволить объектам наследовать свойства других объектов, назначая создаваемым объектам их прототип. Новый объект, таким образом, сможет получить доступ к новым свойствам, пройдя по цепочке прототипов.
С помощью паттерна Prototype можно легко получать доступ и наследовать свойства от других объектов. Поскольку цепочка прототипов позволяет нам обращаться к свойствам, которые не принадлежат объекту напрямую, мы можем избежать дублирования методов и свойств, что уменьшит потребление памяти.