Проверка свойств: in vs hasOwnProperty в JS
Дан объект:
const object = {
foo: 1
};
Если выполнить:
const object = {
foo: 1
};
console.log([
"foo" in object,
"toString" in object,
object.hasOwnProperty("foo"),
object.hasOwnProperty("toString"),
]);
Какие логические значения будут получены в консоли?
Теория и разбор по шагам
Дан объект const object = { foo: 1 };.
У него есть собственное (own) свойство foo, а свойство toString обычно доступно через прототип Object.prototype.
Оператор in возвращает true, если свойство существует в объекте или в его прототипной цепочке.
Метод hasOwnProperty проверяет только собственные свойства объекта (то есть объявленные прямо в нём, а не унаследованные).
{}, как правило, наследуется от Object.prototype, поэтому методы вроде toString «видны» даже без явного объявления в самом объекте."foo" in object
- В самом объекте есть собственное свойство
fooсо значением1. - Следовательно, проверка
"foo" in objectдаётtrue(свойство найдено сразу на объекте).
Результат: true
"toString" in object
- В самом объекте свойства
toStringне объявлено. - Далее поиск идёт по прототипной цепочке:
object -> Object.prototype -> null. - На
Object.prototypeсуществует методtoString, значит"toString" in objectдаётtrue.
Результат: true
object.hasOwnProperty("foo")
hasOwnPropertyотвечает на вопрос: «является ли свойство собственным у этого объекта (а не унаследованным)?».foo— собственное свойство объекта, значит результатtrue.
Результат: true
object.hasOwnProperty("toString")
toStringдоступен, но обычно унаследован отObject.prototype, а не находится прямо в объекте.- Поэтому
object.hasOwnProperty("toString")возвращаетfalse.
Результат: false
Итоговый массив логических значений:[true, true, true, false]
Наглядная схема поиска свойства
Схема того, как движок ищет свойство (упрощённо):
object (own properties)
- foo: 1
- (toString отсутствует)
|
v
Object.prototype
- toString: function ...
|
v
null (конец цепочки)
Логика поиска для проверки наличия:
- Для
in: достаточно, чтобы свойство встретилось на любом звене цепочки. - Для
hasOwnProperty: учитывается только самый первый объект (own properties).
Таблица: in и hasOwnProperty
| Проверка | Что считается «есть свойство» | Идея простыми словами | Пример для toString |
|---|---|---|---|
"prop" in obj | Собственное или унаследованное | «видно ли свойство объекту вообще» | true, так как унаследовано |
obj.hasOwnProperty("prop") | Только собственное | «объявлено ли прямо в объекте» | false, так как не собственное |
hasOwnProperty не является полностью «безопасным»: в объект можно добавить собственное свойство с таким именем и тем самым нарушить обычный вызов obj.hasOwnProperty(...). Более надёжны Object.hasOwn(obj, key) или Object.prototype.hasOwnProperty.call(obj, key).Пример надёжной проверки (показаны оба подхода):
const object = { foo: 1 };
console.log(Object.hasOwn(object, "foo")); // true
console.log(Object.hasOwn(object, "toString")); // false
console.log(Object.prototype.hasOwnProperty.call(object, "foo")); // true
console.log(Object.prototype.hasOwnProperty.call(object, "toString")); // false
Дополнительный пример, который показывает разницу особенно явно (объект без Object.prototype в цепочке):
const dict = Object.create(null);
dict.foo = 1;
console.log("foo" in dict); // true
console.log("toString" in dict); // false
console.log(dict.hasOwnProperty); // undefined
Кратко: foo является собственным свойством, поэтому "foo" in object и object.hasOwnProperty("foo") дают true; toString обычно унаследован через прототип, поэтому "toString" in object даёт true, а object.hasOwnProperty("toString") даёт false — итог [true, true, true, false].