Прототипы JS: что выведет object.property после изменений
Дан код:
const object = {};
object.constructor.prototype.property = 1;
object.constructor.prototype = null;
console.log(object.property);
Какое значение выведет console.log ?
Разбор по шагам
Дан код:
const object = {};
object.constructor.prototype.property = 1;
object.constructor.prototype = null;
console.log(object.property);
- После
const object = {};создаётся обычный объект.
У такого объекта внутренний прототип (внутренний слот)[[Prototype]]указывает наObject.prototype(стандартный прототип для объектных литералов). - Выражение
object.constructorобычно не является собственным свойствомobject.
Оно находится через прототип: берётсяconstructorизObject.prototype, и результатом становится функция-конструкторObject. - Строка
object.constructor.prototype.property = 1;означает:
- берётся функция
Object, - у неё берётся свойство
prototype(это и есть объектObject.prototypeна данный момент), - в этот объект добавляется новое свойство
propertyсо значением1.
Итог: в Object.prototype появляется property, доступное через наследование многим объектам.
- Строка
object.constructor.prototype = null;переопределяет свойствоprototypeу функцииObject.
Это меняет «указатель»Object.prototype(как свойство функцииObject), но не переподключает прототип у уже созданных объектов. - Вызов
console.log(object.property)запускает поиск свойстваproperty:
- в собственных свойствах
objectего нет, - затем поиск идёт в
object.[[Prototype]], который всё ещё указывает на старый объектObject.prototype(в который ранее было добавленоproperty = 1), - поэтому находится значение
1и оно выводится.
Object.prototype и добавление свойств в Object.prototype является потенциально опасной практикой: такие изменения влияют на большое количество объектов и могут приводить к трудноотлавливаемым ошибкам.Наглядная схема того, что происходит:
Шаг 1:
object
[[Prototype]] ---> Object.prototype (старый объект-прототип)
Шаг 3:
Object.prototype.property = 1
Шаг 4:
Object.prototype (как свойство функции Object) = null
НО:
object.[[Prototype]] по-прежнему ---> старый Object.prototype, где property = 1
Теория: prototype и [[Prototype]]
В JavaScript важно различать две похожие по названию, но разные сущности.
obj.[[Prototype]](внутренний прототип объекта)
- Это «скрытая ссылка» конкретного объекта на другой объект.
- Именно по этой ссылке движок JavaScript строит цепочку прототипов при поиске свойств:
obj.prop.
F.prototype(свойствоprototypeу функции)
- Это обычное свойство функции (функция в JavaScript тоже объект).
- Оно используется оператором
newпри создании новых объектов: новый объект получает[[Prototype]], равный текущему значениюF.prototype.
Ключевой смысл задачи:
- Добавление
propertyв текущий объектObject.prototypeвлияет на доступностьobject.property, потому чтоobjectуже наследуется от этого объекта-прототипа. - Присваивание
Object.prototype = null(то естьobject.constructor.prototype = null) влияет на то, что будет считатьсяprototypeу функцииObjectпосле этой строки, но не перепривязывает[[Prototype]]уже созданногоobject.
Как работает чтение object.property:
- Проверяются собственные свойства
object. - Если не найдено — проверяется
object.[[Prototype]]. - Далее —
object.[[Prototype]].[[Prototype]]и так далее, пока не встретитсяnull. - Если цепочка закончилась (дошли до
null) и свойство не найдено — результатом будетundefined.
В данной задаче цепочка не заканчивается на null до нахождения property, потому что property лежит в старом объекте-прототипе.
constructor не является «магической командой», оно тоже ищется по цепочке прототипов как обычное свойство, если не задано напрямую в объекте.Таблица и примеры
Таблица влияния строк кода:
| Строка | Что меняется | Влияние на object.property |
|---|---|---|
object.constructor.prototype.property = 1; | Мутируется объект-прототип (в него добавляется property) | Да, потому что object наследуется от этого прототипа |
object.constructor.prototype = null; | Меняется значение prototype у функции Object | Нет, уже созданный object продолжает ссылаться на старый [[Prototype]] |
Пример, показывающий разницу между «изменить объект-прототип» и «заменить ссылку на prototype»:
function A() {}
const x = new A();
A.prototype.p = 10; // изменение существующего прототип-объекта
console.log(x.p); // 10
A.prototype = { p: 20 }; // замена ссылки на новый объект
console.log(x.p); // всё ещё 10, потому что x.[[Prototype]] не поменялся
const y = new A();
console.log(y.p); // 20, потому что y создан после замены A.prototype
Пример, иллюстрирующий, что constructor обычно берётся из прототипа:
const o = {};
console.log(o.hasOwnProperty('constructor')); // false (в большинстве случаев)
console.log(typeof o.constructor); // function
Кратко: сначала property = 1 добавляется в старый Object.prototype, от которого уже наследуется object, а затем Object.prototype (как свойство функции Object) переопределяется на null без изменения [[Prototype]] уже созданного объекта; поэтому console.log(object.property) выводит 1.