Какое значение выведет консоль с 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, может неожиданно влиять на огромное количество объектов, включая библиотечные компоненты и сторонние модули. Несоответствующие или конфликтующие изменения способны нарушить работоспособность всего проекта. - Проблемы производительности: Глубокая вложенность цепочек прототипов увеличивает нагрузку на систему при поиске свойств. Частые обращения к высокорасположенным уровням могут замедлить выполнение операций, ухудшив общую производительность приложения.
- Трудности диагностики ошибок: Ошибки, вызванные изменением прототипов, сложно локализовать, так как они влияют на множественные объекты одновременно. Их проявления иногда трудно предсказать заранее, что усложняет выявление первопричины сбоя.