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

У всех ли сущностей есть prototype

2.0 Middle🔥 151 комментариев
#JavaScript Core#Архитектура и паттерны

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

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

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

Отличный и очень глубокий вопрос, который касается фундаментальных принципов JavaScript. Короткий ответ: НЕТ, не у всех сущностей в JavaScript есть prototype.

Более развернутый ответ требует понимания различия между объектами, созданными с помощью new и функции-конструктора, и "чистыми" объектами, а также примитивами. Давайте разберем это подробно.

Кому принадлежит prototype?

Свойство prototype есть ТОЛЬКО у функций, которые могут быть использованы как конструкторы (за некоторыми исключениями). Это свойство является объектом, который будет использован как прототип для всех экземпляров, созданных с помощью этой функции и оператора new.

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

// У функции Person ЕСТЬ свойство prototype
console.log(Person.prototype); // {constructor: ƒ}

const john = new Person('John');
// У созданного объекта (экземпляра) НЕТ своего свойства prototype
console.log(john.prototype); // undefined

У кого есть prototype?

  1. Обычные функции (объявленные через function).
  2. Классы (ES6+). Под капотом класс — это "синтаксический сахар" для функции-конструктора.
  3. Стрелочные функции — НЕТ. Они не могут быть конструкторами.
  4. Методы объекта — НЕТ (если это не обычная функция, записанная как значение свойства).
  5. Генераторы и async-функции — Да, у них есть prototype, но они не предназначены для использования как конструкторы (вызов с new выбросит ошибку).
// Проверим наличие prototype
const arrowFunc = () => {};
console.log(arrowFunc.prototype); // undefined

const obj = {
  method() {}
};
console.log(obj.method.prototype); // undefined

class Animal {}
console.log(Animal.prototype); // {constructor: ƒ}

[[Prototype]] vs prototype. Связующее звено

Здесь ключ к пониманию — не путать свойство prototype функции со внутренним слотом [[Prototype]] (доступным через __proto__ или Object.getPrototypeOf()) любого объекта.

  • prototype — это свойство функции-конструктора, которое указывает на объект, который станет прототипом для будущих экземпляров.
  • [[Prototype]] (или __proto__) — это внутренняя ссылка ЛЮБОГО объекта на его прототип. Это то, что используется при поиске свойств по цепочке прототипов.
function Car(model) {
  this.model = model;
}
// Добавляем метод в prototype функции Car
Car.prototype.drive = function() { console.log('Vroom!'); };

const myCar = new Car('Tesla');

// У экземпляра myCar НЕТ своего свойства .prototype
console.log(myCar.prototype); // undefined

// Но у него ЕСТЬ внутренняя ссылка [[Prototype]] (__proto__),
// которая ссылается на Car.prototype
console.log(Object.getPrototypeOf(myCar) === Car.prototype); // true
console.log(myCar.__proto__ === Car.prototype); // true (устаревший способ)

// Именно благодаря этой цепочке myCar имеет доступ к методу drive
myCar.drive(); // 'Vroom!'

Сущности, у которых ТОЧНО НЕТ prototype

  1. Объекты, созданные с помощью литерала {} или new Object(). У них есть [[Prototype]] (который равен Object.prototype), но нет своего свойства prototype.

    const obj = {};
    console.log(obj.prototype); // undefined
    console.log(Object.getPrototypeOf(obj)); // Object.prototype
    
  2. Экземпляры, созданные через new. Как в примере с Car и myCar.

  3. Примитивные значения (string, number, boolean, null, undefined, symbol, bigint). Когда мы пытаемся обратиться к свойству примитива, JavaScript временно оборачивает его в объект-обертку (String, Number и т.д.), у которого уже есть prototype. Но сам примитив свойства prototype не имеет.

    const str = "hello";
    console.log(str.prototype); // undefined
    // Но мы можем использовать методы, потому что происходит автоупаковка:
    // Временный объект String(str) имеет в цепочке String.prototype
    console.log(str.toUpperCase()); // "HELLO"
    
  4. null и undefined. Это особые примитивы, у них нет не только prototype, но и никаких свойств вообще. Любая попытка обращения к свойству выбросит ошибку.

Проверка на "обладание" prototype

Практический способ проверить, можно ли использовать сущность как конструктор (и есть ли у нее prototype), — это посмотреть на ее дескриптор свойства prototype.

function hasOwnPrototype(entity) {
  // 1. У примитивов и null/undefined нет свойств
  if (entity === null || (typeof entity !== 'object' && typeof entity !== 'function')) {
    return false;
  }
  // 2. Проверяем, есть ли у сущности собственное (own) свойство 'prototype'
  // и является ли оно перечислимым и записываемым (как у обычных функций).
  const descriptor = Object.getOwnPropertyDescriptor(entity, 'prototype');
  return descriptor !== undefined && descriptor.writable === true && descriptor.enumerable === false;
}

console.log(hasOwnPrototype(function(){})); // true
console.log(hasOwnPrototype(class {}));     // true
console.log(hasOwnPrototype({}));           // false
console.log(hasOwnPrototype([]));           // false
console.log(hasOwnPrototype(() => {}));     // false
console.log(hasOwnPrototype('string'));     // false

Итог

  • Свойство prototype — это особенность функций, предназначенных для создания объектов (конструкторов).
  • Внутренняя ссылка [[Prototype]] (цепочка прототипов) есть у подавляющего большинства объектов, кроме Object.create(null).
  • Примитивы, "простые" объекты и экземпляры не имеют собственного свойства prototype. Их поведение определяется цепочкой прототипов ([[Prototype]]), которая берет свое начало от prototype функции-конструктора, их создавшей.

Понимание этого различия критически важно для глубокого владения языком, работы с наследованием, классами и для написания эффективного и предсказуемого кода.

У всех ли сущностей есть prototype | PrepBro