Object.create(null) и hasOwnProperty: что вернётся
Что вернёт следующий код?
console.log(Object.create(null).hasOwnProperty('toString'))
Теория и разбор
Метод hasOwnProperty является методом Object.prototype и доступен «обычным» объектам через цепочку прототипов, а не как автоматически созданное собственное свойство каждого объекта.
Объекты, созданные через Object.create(null), имеют null в качестве [[Prototype]], поэтому не наследуют методы Object.prototype, включая hasOwnProperty.
Код из условия:
console.log(Object.create(null).hasOwnProperty('toString'))
Последовательность вычисления (упрощённо, но по смыслу корректно):
- Выражение
Object.create(null)создаёт объект без прототипа ([[Prototype]] = null). - Доступ к свойству
.hasOwnPropertyвозвращаетundefined, потому что это свойство не найдено ни в самом объекте, ни в прототипе (прототипа нет). - Затем выполняется попытка вызвать
undefinedкак функцию:undefined('toString'), что приводит кTypeErrorещё до выполненияconsole.log(...)(аргумент не успевает вычислиться успешно).
'toString' выбран как пример «из прототипа»: у обычного объекта {} он обычно доступен через Object.prototype, но у null-прототипа прототипной цепочки нет.Почему возникает TypeError
Ошибка появляется не из‑за строки 'toString', а из‑за отсутствия самой функции hasOwnProperty у объекта с null‑прототипом.
Фактически происходит попытка выполнить вызов у значения undefined, что в JavaScript приводит к TypeError: ... is not a function.
Схема прототипов:
Обычный объект:
obj ---> Object.prototype ---> null
| |
| +-- hasOwnProperty()
+-- (собственные свойства)
Null-прототип:
dict ---> null
|
+-- (только собственные свойства, без методов Object.prototype)
Как проверить свойство правильно
Для проверки «собственного» свойства у объекта без прототипа корректно применять Object.hasOwn(obj, prop), так как он работает и с null‑prototype объектами.
Также корректен универсальный классический подход через заимствование метода: Object.prototype.hasOwnProperty.call(obj, prop).
Примеры корректных подходов:
// 1) Современный вариант (предпочтительный)
const obj = Object.create(null);
obj.toString = 123;
console.log(Object.hasOwn(obj, 'toString')); // true
// 2) Классический универсальный вариант
const obj2 = Object.create(null);
console.log(Object.prototype.hasOwnProperty.call(obj2, 'toString')); // false
// 3) Ещё один вариант «взять hasOwnProperty у обычного объекта»
const obj3 = Object.create(null);
console.log(({}).hasOwnProperty.call(obj3, 'toString')); // false
Таблица различий {} и Object.create(null):
| Признак | {} | Object.create(null) |
|---|---|---|
[[Prototype]] | Object.prototype | null |
Доступен ли obj.hasOwnProperty(...) | Да (унаследован) | Нет, будет TypeError при вызове |
Работает ли Object.hasOwn(obj, 'x') | Да | Да |
Есть ли унаследованный toString | Обычно да, через прототип | Нет, прототипной цепочки нет |
obj.hasOwnProperty(key) напрямую, потому что:null‑прототипом метода нет;hasOwnProperty, которое «затенит» метод и приведёт к ошибкам или неожиданному поведению.Поэтому безопаснее использовать
Object.hasOwn(obj, key) или Object.prototype.hasOwnProperty.call(obj, key).Итого: выражение Object.create(null).hasOwnProperty('toString') завершится TypeError, потому что у объекта с null‑прототипом нет унаследованного метода hasOwnProperty, а попытка вызвать undefined как функцию приводит к ошибке.