`__proto__` vs `prototype`

prototype — свойство функции-конструктора, задающее прототип будущих объектов; __proto__ — внутренняя ссылка каждого объекта на его прототип в цепочке наследования.

Зачем нужно

Понимание разницы между prototype и __proto__ необходимо для понимания прототипного наследования — основного механизма ООП в JavaScript. Без этого невозможно осознанно использовать классы, Object.create, instanceof и отлаживать цепочки прототипов.

Где используется

  • Определение методов на прототипе функции-конструктора
  • Проверка цепочки прототипов через instanceof и isPrototypeOf
  • Object.create(proto) — создание объекта с заданным прототипом
  • Расширение встроенных классов (с осторожностью)

Концепция

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

__proto__ — устаревший accessor, открывающий доступ к [[Prototype Pattern]] конкретного объекта. Современная альтернатива — Object.getPrototypeOf / Object.setPrototypeOf.

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

Animal.prototype.speak = function {
  return `${this.name} говорит`;
};

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

// prototype — свойство функции
console.log(Animal.prototype);         // { speak: f, constructor: Animal }

// __proto__ — ссылка экземпляра на прототип
console.log(dog.__proto__ === Animal.prototype); // true

// Современный способ
console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true

Цепочка прототипов (prototype chain)

function Vehicle(type) {
  this.type = type;
}
Vehicle.prototype.describe = function {
  return `Транспорт: ${this.type}`;
};

function Car(brand) {
  Vehicle.call(this, 'car');
  this.brand = brand;
}
// Настраиваем наследование
Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;

Car.prototype.honk = function {
  return `${this.brand}: бип!`;
};

const bmw = new Car('BMW');
console.log(bmw.describe); // "Транспорт: car" — из Vehicle.prototype
console.log(bmw.honk);    // "BMW: бип!" — из Car.prototype

// Цепочка: bmw.__proto__ → Car.prototype → Vehicle.prototype → Object.prototype → null

Object.create vs new

const animalProto = {
  speak {
    return `${this.name} говорит`;
  }
};

// Object.create явно задаёт прототип
const cat = Object.create(animalProto);
cat.name = 'Мурка';
console.log(cat.speak); // "Мурка говорит"
console.log(Object.getPrototypeOf(cat) === animalProto); // true

Проверка принадлежности прототипу

console.log(bmw instanceof Car);        // true
console.log(bmw instanceof Vehicle);    // true
console.log(Car.prototype.isPrototypeOf(bmw)); // true

Частые ошибки

  • Мутация prototype после создания объектов — замена всего объекта Constructor.prototype = {...} обрывает связь с уже созданными экземплярами; нужно добавлять свойства, а не перезаписывать объект, или восстанавливать constructor.
  • Прямое изменение __proto__ — очень медленная операция, разрушает оптимизации движка; используйте Object.create или Object.setPrototypeOf при инициализации.
  • Путаница prototype у обычного объекта — обычные объекты не имеют prototype (только функции); у них есть __proto__.

Связанные темы

Ресурсы