Какое значение выведет консоль с object.property?

Дан код:

const object = {};
object.constructor.prototype.property = 1;
object.constructor.prototype = null;
console.log(object.property); 

Какое значение выведет console.log ?

Теория по задаче

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

Каждый объект имеет внутренний прототип ([[Prototype]])

Любой объект в JavaScript хранит скрытую ссылку на другой объект, называемую прототипом ([[Prototype]]). Эту ссылку можно считать аналогом родительского класса в классических языках программирования.

Получить доступ к внутренней ссылке на прототип можно через устаревшее свойство __proto__ или современные методы Object.getPrototypeOf() и Object.setPrototypeOf().

Внутренняя ссылка позволяет организовать наследование: при попытке обратиться к несуществующему свойству объекта начинается автоматический поиск нужного свойства среди цепочки прототипов.

Иерархия прототипов («цепочка прототипов»)

Каждый объект наследует свойства и методы от своего прототипа. Если нужное свойство не найдено в самом объекте, система автоматически ищет его в его прототипе. Если и там нет, поиск идёт дальше по цепочке прототипов.

Цепочка заканчивается, когда достигнута вершина — объект Object.prototype или же когда встречается null, означающее отсутствие дальнейших прототипов.

Пример структуры:

const person = {};  // __proto__ → Object.prototype
Object.prototype.isPrototypeOf(person);  // true

Все объекты создаются с прототипом

Большинство объектов наследуют от стандартного объекта-прототипа Object.prototype, содержащего полезные методы вроде toString(), hasOwnProperty() и др.

Объекты, созданные через литералы вида { }, имеют своим прототипом именно Object.prototype.

Конструктор и его прототип

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

Новый объект, созданный с помощью оператора new, получает в качестве внутреннего прототипа ([[Prototype]]) именно объект, указанный в свойстве prototype соответствующего конструктора.

Например:

function User() {}  // конструктор
User.prototype.greet = function() { return "Hello!" };
const user = new User();  // user.__proto__ → User.prototype
user.greet();  // "Hello!"

Методы расширения и наследования

Любые изменения, внесённые в прототип конструктора, немедленно отражаются на всех объектах, созданных этим конструктором. Это позволяет удобно добавлять методы и свойства, доступные всем экземплярам.

Метод Object.create() создаёт новый объект с заданным прототипом, обеспечивая лёгкое построение цепочек наследования.

Запрет на изменение некоторых аспектов

Хотя разработчики могут свободно устанавливать и изменять большинство свойств объектов, некоторые ключевые элементы защищены специальными дескрипторами свойств. Например, свойство prototype функции-конструктора защищает себя от перезаписи, так как оно помечено как непереописуемое (writable: false).

Пример попыток изменить прототип:

function MyClass() {}
MyClass.prototype.prop = 1;  // работает нормально
MyClass.prototype = null;  // НЕ сработает, так как protype не переопределяется

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

  • Object.getPrototypeOf(obj) возвращает непосредственного прототипа указанного объекта.
  • Object.setPrototypeOf(obj, newProto) позволяет сменить прототип объекта на новый.

Строго говоря, прямая работа с прототипами должна проводиться осторожно, так как неправильное использование может привести к нежелательным последствиям. Возможные негативные последствия неправильного использования прототипов:

  • Разрыв цепочки прототипов: Неправильное назначение прототипа (например, прямой перезаписью через __proto__) может прервать нормальную работу приложения. Особенно опасно полное обнуление прототипа через выражение вроде obj.__proto__ = null: это лишает объект доступа ко всей цепочке стандартных методов и свойств, вызывая непредсказуемость поведения проекта.
  • Перезапись базовых методов: Добавление или изменение свойств на общем прототипе, таком как Object.prototype, может неожиданно влиять на огромное количество объектов, включая библиотечные компоненты и сторонние модули. Несоответствующие или конфликтующие изменения способны нарушить работоспособность всего проекта.
  • Проблемы производительности: Глубокая вложенность цепочек прототипов увеличивает нагрузку на систему при поиске свойств. Частые обращения к высокорасположенным уровням могут замедлить выполнение операций, ухудшив общую производительность приложения.
  • Трудности диагностики ошибок: Ошибки, вызванные изменением прототипов, сложно локализовать, так как они влияют на множественные объекты одновременно. Их проявления иногда трудно предсказать заранее, что усложняет выявление первопричины сбоя.