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

Что такое Proto?

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

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

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

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

Что такое Proto (proto и Prototype)

Etот вопрос проверяет понимание одного из самых фундаментальных механизмов JavaScript — прототипного наследования. После 10+ лет разработки скажу: это ключ к пониманию того, как работает весь язык.

Важное уточнение: proto vs prototype

Это две разные вещи, которые часто путают:

  • prototype — свойство, которое есть у функций и классов
  • __proto__ — скрытое свойство объекта, указывающее на его прототип

Что такое Prototype

prototype — это объект, который содержит методы и свойства, которые должны быть доступны всем экземплярам класса или функции-конструктора.

// Функция-конструктор
function Animal(name) {
  this.name = name;
}

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

const dog = new Animal("Шарик");
dog.speak(); // Шарик издаёт звук

console.log(Animal.prototype); // { speak: [Function], constructor: Animal }

Что такое proto

__proto__ — это скрытая ссылка на прототип объекта. Когда ты получаешь доступ к свойству, JavaScript сначала ищет его в самом объекте, потом в __proto__, потом в __proto__.__proto__ и так далее.

const dog = new Animal("Шарик");

console.log(dog.__proto__ === Animal.prototype); // true
console.log(dog.speak); // ищет в dog → dog.__proto__ → находит

// Цепочка прототипов
console.log(dog.__proto__.__proto__ === Object.prototype); // true
console.log(dog.__proto__.__proto__.__proto__); // null

Прототипная цепочка (Prototype Chain)

Это механизм, благодаря которому работает наследование в JavaScript:

function Animal(name) {
  this.name = name;
}

Animal.prototype.describe = function() {
  return `Это ${this.name}`;
};

function Dog(name, breed) {
  Animal.call(this, name);
  this.breed = breed;
}

// Создаём прототипную цепочку
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
  return `${this.name} лает`;
};

const dog = new Dog("Шарик", "Лабрадор");

console.log(dog.name);      // Шарик (собственное свойство)
console.log(dog.bark());     // Шарик лает (из Dog.prototype)
console.log(dog.describe()); // Это Шарик (из Animal.prototype)

// Цепочка: dog → Dog.prototype → Animal.prototype → Object.prototype → null

Современный способ: классы

ES6 классы — это синтаксический сахар над прототипами. Под капотом работает точно то же прототипное наследование:

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  speak() {
    console.log(`${this.name} издаёт звук`);
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  
  bark() {
    console.log(`${this.name} лает`);
  }
}

const dog = new Dog("Шарик", "Лабрадор");
dog.speak(); // Шарик издаёт звук
dog.bark();  // Шарик лает

// Под капотом
console.log(dog.__proto__ === Dog.prototype);           // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true

Копирование прототипа vs наследование

Важное различие:

// ❌ Неправильно — копируем прототип
Dog.prototype = Animal.prototype;
// Проблема: изменения в Dog.prototype повлияют на Animal

// ✅ Правильно — создаём новый объект
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Проверка прототипа

const dog = new Dog("Шарик");

// instanceof — проверяет прототипную цепочку
console.log(dog instanceof Dog);    // true
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true

// isPrototypeOf — проверяет, есть ли объект в цепочке
console.log(Animal.prototype.isPrototypeOf(dog)); // true

// Object.getPrototypeOf — получить прототип
console.log(Object.getPrototypeOf(dog) === Dog.prototype); // true

Практические примеры в React

// Класс с методами через prototype
class BaseComponent {
  formatDate(date: Date) {
    return date.toLocaleDateString();
  }
}

class UserCard extends BaseComponent {
  render(user: User) {
    return (
      <div>
        <h2>{user.name}</h2>
        <p>Создан: {this.formatDate(user.createdAt)}</p>
      </div>
    );
  }
}

// Но в React предпочитают функциональные компоненты и композицию
interface BaseComponentProps {
  formatDate: (date: Date) => string;
}

function UserCard({ user, formatDate }: BaseComponentProps & { user: User }) {
  return (
    <div>
      <h2>{user.name}</h2>
      <p>Создан: {formatDate(user.createdAt)}</p>
    </div>
  );
}

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

// ✅ Методы в прототипе (экономит память)
function User(name) {
  this.name = name;
}
User.prototype.getName = function() {
  return this.name;
};

const users = Array.from({ length: 1000000 }, (_, i) => new User(`User${i}`));
// Метод getName существует один раз в памяти, общий для всех экземпляров

// ❌ Методы в конструкторе (дублирование в памяти)
function BadUser(name) {
  this.name = name;
  this.getName = function() { // каждый экземпляр имеет свою копию
    return this.name;
  };
}

Модификация встроенных прототипов (избегай этого)

// ❌ Очень плохо — модифицируем встроенный тип
Array.prototype.myMethod = function() {};
// Это может сломать код других библиотек

// ✅ Хорошо — создавай свои классы
class MyArray extends Array {
  myMethod() {}
}

Заключение

__proto__ и prototype — это основа прототипного наследования в JavaScript. Понимание того, как работает прототипная цепочка, критично для:

  • Понимания того, как работают классы ES6
  • Отладки проблем с this и наследованием
  • Оптимизации производительности (методы в прототипе экономят память)
  • Использования Object.create и других встроенных методов

Хотя в современной разработке мы чаще используем классы и функциональные компоненты, понимание прототипного механизма остаётся фундаментальным для компетентной работы с JavaScript.