Проверка свойств: 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

  1. В самом объекте есть собственное свойство foo со значением 1.
  2. Следовательно, проверка "foo" in object даёт true (свойство найдено сразу на объекте).

Результат: true

"toString" in object

  1. В самом объекте свойства toString не объявлено.
  2. Далее поиск идёт по прототипной цепочке: object -> Object.prototype -> null.
  3. На Object.prototype существует метод toString, значит "toString" in object даёт true.

Результат: true

object.hasOwnProperty("foo")

  1. hasOwnProperty отвечает на вопрос: «является ли свойство собственным у этого объекта (а не унаследованным)?».
  2. foo — собственное свойство объекта, значит результат true.

Результат: true

object.hasOwnProperty("toString")

  1. toString доступен, но обычно унаследован от Object.prototype, а не находится прямо в объекте.
  2. Поэтому 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].