← Назад к вопросам

Как создать прототип?

2.0 Middle🔥 141 комментариев
#JavaScript Core

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Создание прототипов в JavaScript

В JavaScript прототип (prototype) — это механизм наследования, на котором построена вся объектно-ориентированная система языка. Каждая функция-конструктор (кроме стрелочных) имеет свойство .prototype, а каждый объект — скрытое свойство [[Prototype]] (доступное через __proto__ или методы Object.getPrototypeOf()).

Основные способы создания прототипов

1. Использование конструктора функции

Самый классический подход, унаследованный из ранних версий JavaScript:

// Создаем конструктор
function Animal(name) {
  this.name = name;
}

// Добавляем методы в прототип
Animal.prototype.speak = function() {
  console.log(`${this.name} издает звук`);
};

// Создаем экземпляр
const dog = new Animal('Барсик');
dog.speak(); // "Барсик издает звук"

// Проверяем прототипную цепочку
console.log(dog.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true

2. Современный синтаксис классов (ES6+)

Классы в JavaScript — это "синтаксический сахар" над прототипным наследованием:

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} издает звук`);
  }
  
  // Статический метод (не наследуется экземплярами)
  static describe() {
    return 'Это класс животных';
  }
}

// Наследование через extends
class Dog extends Animal {
  constructor(name, breed) {
    super(name); // Вызов конструктора родителя
    this.breed = breed;
  }
  
  speak() {
    console.log(`${this.name} лает: Гав-гав!`);
  }
}

const rex = new Dog('Рекс', 'Овчарка');
rex.speak(); // "Рекс лает: Гав-гав!"
console.log(rex instanceof Animal); // true

3. Использование Object.create()

Прямое создание объекта с указанным прототипом:

// Базовый прототип
const animalPrototype = {
  speak() {
    console.log(`${this.name} издает звук`);
  },
  init(name) {
    this.name = name;
    return this;
  }
};

// Создаем объект с указанным прототипом
const cat = Object.create(animalPrototype).init('Мурзик');
cat.speak(); // "Мурзик издает звук"

// Более сложная цепочка прототипов
const felinePrototype = Object.create(animalPrototype);
felinePrototype.purr = function() {
  console.log(`${this.name} мурлычет`);
};

const lion = Object.create(felinePrototype).init('Симба');
lion.speak(); // Унаследовано от animalPrototype
lion.purr();  // Унаследовано от felinePrototype

4. Factory функции с прототипами

Комбинация фабричных функций и прототипов:

function createAnimal(name) {
  const animal = Object.create(animalMethods);
  animal.name = name;
  return animal;
}

const animalMethods = {
  speak() {
    console.log(`${this.name} издает звук`);
  },
  eat(food) {
    console.log(`${this.name} ест ${food}`);
  }
};

const bird = createAnimal('Чижик');
bird.speak(); // "Чижик издает звук"

Работа с прототипами

Изменение и расширение прототипов

// Добавление метода всем массивам (осторожно!)
Array.prototype.customMap = function(callback) {
  const result = [];
  for (let i = 0; i < this.length; i++) {
    result.push(callback(this[i], i, this));
  }
  return result;
};

const numbers = [1, 2, 3];
const doubled = numbers.customMap(n => n * 2);
console.log(doubled); // [2, 4, 6]

Важно: Модификация встроенных прототипов считается антипаттерном, так как может вызывать конфликты в больших приложениях и библиотеках.

Проверка прототипных связей

class Vehicle {}
class Car extends Vehicle {}

const myCar = new Car();

console.log(myCar instanceof Car);      // true
console.log(myCar instanceof Vehicle);  // true
console.log(myCar instanceof Object);   // true

console.log(Object.getPrototypeOf(myCar) === Car.prototype); // true
console.log(Car.prototype.isPrototypeOf(myCar)); // true

Практические рекомендации

  1. Используйте классы для сложных иерархий — они обеспечивают чистый и понятный синтаксис
  2. Object.create() для композиции — когда нужно гибкое комбинирование поведения
  3. Избегайте модификации встроенных прототипов — кроме случаев создания полифилов для устаревших браузеров
  4. Понимайте разницу между __proto__ и prototype:
    • prototype — свойство функций-конструкторов
    • __proto__ — свойство экземпляров, ссылающееся на прототип конструктора
  5. Используйте статические методы для функциональности, связанной с классом, но не с экземплярами

Производительность и память

Одно из ключевых преимуществ прототипного наследования — эффективное использование памяти. Методы хранятся в прототипе и разделяются между всеми экземплярами, в отличие от создания копий методов для каждого объекта.

// Неэффективно: каждый экземпляр получает свою копию метода
function InefficientAnimal(name) {
  this.name = name;
  this.speak = function() { /* ... */ };
}

// Эффективно: метод находится в прототипе
function EfficientAnimal(name) {
  this.name = name;
}
EfficientAnimal.prototype.speak = function() { /* ... */ };

Прототипная система JavaScript — мощный и гибкий инструмент, который, при правильном использовании, позволяет создавать эффективные, поддерживаемые и масштабируемые архитектуры приложений.