Vue Front-end Инженер

Vue Front-end Инженер

Роадмап навыков для прокачки

Метод Object.hasOwn() вместо hasOwnProperty

JavaScript (ES6 и новее)Общие возможности ES6По умолчанию

Основная идея

Object.hasOwn() — современный статический метод для проверки наличия собственного свойства объекта, который безопаснее и проще в использовании чем hasOwnProperty().

Ключевые аспекты

  • СинтаксисObject.hasOwn(obj, prop) вместо obj.hasOwnProperty(prop)
  • Безопасность — работает с объектами без прототипа (Object.create(null))
  • Надёжность — не зависит от переопределения hasOwnProperty в объекте
  • ES2022 — добавлен в стандарт ECMAScript 2022
  • Возвращаетtrue если свойство собственное, false если унаследованное или отсутствует

Плюсы Object.hasOwn()

  • Работает с любыми объектами, включая Object.create(null)
  • Защищён от переопределения метода в объекте
  • Более читаемый синтаксис
  • Статический метод — не требует вызова через прототип

Минусы

  • Требует полифилл для старых браузеров
  • Менее распространён в существующем коде
  • Только ES2022+

Частые ошибки на собеседованиях

  • Путают собственные и унаследованные свойства
  • Не знают про проблемы hasOwnProperty с объектами без прототипа
  • Забывают, что in оператор проверяет и унаследованные свойства
  • Используют hasOwnProperty напрямую без проверки его существования

Введение и проблематика

Для проверки наличия собственного свойства у объекта традиционно использовался метод hasOwnProperty():

js
const user = { name: 'Иван' };
user.hasOwnProperty('name'); // true
user.hasOwnProperty('toString'); // false (унаследован)

Однако этот подход имеет несколько проблем, которые решает новый метод Object.hasOwn().

Object.hasOwn() появился в ES2022 как более безопасная и удобная альтернатива hasOwnProperty().


Базовая теория

Сигнатура

js
Object.hasOwn(obj, prop)
  • obj — объект для проверки
  • prop — имя свойства (строка или Symbol)
  • Возвращает boolean

Собственные vs унаследованные свойства

graph TD A[Object.prototype] --> B["toString()"] A --> C["hasOwnProperty()"] D[user объект] --> E["name: 'Иван'"] D --> F["age: 25"] D -.-> A style E fill:#51cf66 style F fill:#51cf66 style B fill:#ff6b6b style C fill:#ff6b6b
  • Собственные (зелёные) — определены непосредственно в объекте
  • Унаследованные (красные) — доступны через цепочку прототипов

Практические примеры

Базовое использование

Code Example 1: Что вернёт каждый вызов? Как метод различает собственные, унаследованные и несуществующие свойства?

js
const user = {
  name: 'Иван',
  age: 25
};
 
// Проверка собственных свойств
Object.hasOwn(user, 'name');      // true
Object.hasOwn(user, 'age');       // true
 
// Унаследованные свойства — false
Object.hasOwn(user, 'toString');  // false
Object.hasOwn(user, 'valueOf');   // false
 
// Несуществующие свойства — false
Object.hasOwn(user, 'email');     // false

Проблемы hasOwnProperty()

Проблема 1: Объекты без прототипа

Code Example 2: Почему первый вызов выбросит ошибку? Как Object.hasOwn() решает эту проблему?

js
// Создаём объект без прототипа
const dict = Object.create(null);
dict.key = 'value';
 
// ❌ hasOwnProperty не существует!
dict.hasOwnProperty('key'); // TypeError: hasOwnProperty is not a function
 
// ✅ Object.hasOwn() работает
Object.hasOwn(dict, 'key'); // true
⚠️

Объекты Object.create(null) часто используются как словари/кэши для избежания конфликтов с прототипом.

Проблема 2: Переопределение hasOwnProperty

Code Example 3: Какая проблема в этом коде? Почему hasOwnProperty возвращает неверный результат?

js
const obj = {
  hasOwnProperty: () => false, // Злоумышленник переопределил метод
  secret: 'данные'
};
 
// ❌ Вернёт false, хотя свойство есть!
obj.hasOwnProperty('secret'); // false
 
// ✅ Object.hasOwn() невозможно переопределить
Object.hasOwn(obj, 'secret'); // true

Проблема 3: Неудобный синтаксис для безопасного вызова

Code Example 4: Зачем нужен вызов через Object.prototype.hasOwnProperty.call()? Как Object.hasOwn() упрощает код?

js
const data = getSomeData(); // Может быть без прототипа
 
// ❌ Громоздкий безопасный вызов через Object.prototype
Object.prototype.hasOwnProperty.call(data, 'key');
 
// ✅ Чистый и простой вызов
Object.hasOwn(data, 'key');

Пограничные кейсы

Сравнение с оператором in

Code Example 5: Чем отличается оператор in от Object.hasOwn()? Что вернёт каждый вызов?

js
const obj = { name: 'Иван' };
 
// in проверяет ВСЕ свойства (включая унаследованные)
'name' in obj;      // true (собственное)
'toString' in obj;  // true (унаследованное!)
 
// Object.hasOwn проверяет ТОЛЬКО собственные
Object.hasOwn(obj, 'name');      // true
Object.hasOwn(obj, 'toString');  // false

Символы как ключи

Code Example 6: Работает ли Object.hasOwn() с Symbol-ключами? Что вернут вызовы?

js
const sym = Symbol('id');
const obj = {
  [sym]: 123,
  name: 'Иван'
};
 
Object.hasOwn(obj, sym);    // true
Object.hasOwn(obj, 'name'); // true

Геттеры и сеттеры

js
const obj = {
  get computed() {
    return 42;
  }
};
 
// Геттер — это собственное свойство
Object.hasOwn(obj, 'computed'); // true
🚫

Антипаттерн: использование hasOwnProperty без защиты

Code Example 7: Какой из трёх подходов проверки свойства наиболее безопасный и почему?

js
function processData(data) {
  // ❌ Опасно — data может быть Object.create(null)
  if (data.hasOwnProperty('key')) { /* ... */ }
 
  // ❌ Громоздко
  if (Object.prototype.hasOwnProperty.call(data, 'key')) { /* ... */ }
 
  // ✅ Просто и безопасно
  if (Object.hasOwn(data, 'key')) { /* ... */ }
}

Продвинутые аспекты

Использование в итерации

Code Example 8: Зачем нужна проверка Object.hasOwn() внутри for...in? Какая альтернатива существует?

js
const obj = {
  a: 1,
  b: 2,
  c: 3
};
 
// for...in итерирует и унаследованные свойства
for (const key in obj) {
  // Фильтруем только собственные
  if (Object.hasOwn(obj, key)) {
    console.log(key, obj[key]);
  }
}
 
// Альтернатива — Object.keys() (только собственные)
Object.keys(obj).forEach(key => {
  console.log(key, obj[key]);
});

Полифилл

Code Example 9: Как работает полифилл для Object.hasOwn()? Почему внутри используется Object.prototype.hasOwnProperty.call()?

js
if (!Object.hasOwn) {
  Object.defineProperty(Object, 'hasOwn', {
    value: function(obj, prop) {
      if (obj == null) {
        throw new TypeError('Cannot convert undefined or null to object');
      }
      return Object.prototype.hasOwnProperty.call(obj, prop);
    },
    configurable: true,
    enumerable: false,
    writable: true
  });
}

TypeScript

ts
// TypeScript понимает Object.hasOwn() как type guard
const obj: Record<string, unknown> = getData();
 
if (Object.hasOwn(obj, 'name')) {
  // TypeScript знает, что 'name' существует
  console.log(obj.name);
}

Плюсы и минусы

АспектObject.hasOwn()hasOwnProperty()
Object.create(null)✅ Работает❌ TypeError
Переопределение✅ Защищён❌ Уязвим
Синтаксис✅ Чистый❌ Громоздкий для безопасного вызова
Поддержка⚠️ ES2022+✅ Везде
Читаемость✅ Понятный⚠️ Менее очевидный

Вопросы интервьюера

Q: Зачем нужен Object.hasOwn(), если есть hasOwnProperty()?

Object.hasOwn() безопаснее: работает с Object.create(null) и защищён от переопределения метода в объекте.

Q: В чём разница между in и Object.hasOwn()?

in проверяет все свойства включая унаследованные. Object.hasOwn() — только собственные.

Q: Когда hasOwnProperty() выбросит ошибку?

Когда объект создан через Object.create(null) — у него нет прототипа и метода hasOwnProperty.

Q: Как безопасно использовать hasOwnProperty() в старом коде?

Через Object.prototype.hasOwnProperty.call(obj, prop).


Источники

Code Example 1: Object.hasOwn() vs hasOwnProperty()

❓ Что вернёт каждый вызов? Как метод различает собственные, унаследованные и несуществующие свойства?

Object.hasOwn():

js
const user = {
  name: 'Иван',
  age: 25
};
 
Object.hasOwn(user, 'name');
Object.hasOwn(user, 'age');
Object.hasOwn(user, 'toString');
Object.hasOwn(user, 'valueOf');
Object.hasOwn(user, 'email');

hasOwnProperty():

js
const user = {
  name: 'Иван',
  age: 25
};
 
user.hasOwnProperty('name');
user.hasOwnProperty('age');
user.hasOwnProperty('toString');
user.hasOwnProperty('email');

Code Example 2: Объекты без прототипа

❓ Почему первый вызов выбросит ошибку? Как Object.hasOwn() решает эту проблему?

js
const dict = Object.create(null);
dict.key = 'value';
 
dict.hasOwnProperty('key');
 
Object.hasOwn(dict, 'key');

Code Example 3: Переопределение hasOwnProperty

❓ Какая проблема в этом коде? Почему hasOwnProperty возвращает неверный результат?

js
const obj = {
  hasOwnProperty: () => false,
  secret: 'данные'
};
 
obj.hasOwnProperty('secret');
 
Object.hasOwn(obj, 'secret');

Code Example 4: Безопасный вызов

❓ Зачем нужен вызов через Object.prototype.hasOwnProperty.call()? Как Object.hasOwn() упрощает код?

js
const data = getSomeData();
 
Object.prototype.hasOwnProperty.call(data, 'key');
 
Object.hasOwn(data, 'key');

Code Example 5: Оператор in vs Object.hasOwn()

❓ Чем отличается оператор in от Object.hasOwn()? Что вернёт каждый вызов?

js
const obj = { name: 'Иван' };
 
'name' in obj;
'toString' in obj;
 
Object.hasOwn(obj, 'name');
Object.hasOwn(obj, 'toString');

Code Example 6: Symbol-ключи

❓ Работает ли Object.hasOwn() с Symbol-ключами? Что вернут вызовы?

js
const sym = Symbol('id');
const obj = {
  [sym]: 123,
  name: 'Иван'
};
 
Object.hasOwn(obj, sym);
Object.hasOwn(obj, 'name');

Code Example 7: Антипаттерн

❓ Какой из трёх подходов проверки свойства наиболее безопасный и почему?

js
function processData(data) {
  if (data.hasOwnProperty('key')) { /* ... */ }
 
  if (Object.prototype.hasOwnProperty.call(data, 'key')) { /* ... */ }
 
  if (Object.hasOwn(data, 'key')) { /* ... */ }
}

Code Example 8: Использование в итерации

❓ Зачем нужна проверка Object.hasOwn() внутри for...in? Какая альтернатива существует?

js
const obj = {
  a: 1,
  b: 2,
  c: 3
};
 
for (const key in obj) {
  if (Object.hasOwn(obj, key)) {
    console.log(key, obj[key]);
  }
}
 
Object.keys(obj).forEach(key => {
  console.log(key, obj[key]);
});

Code Example 9: Полифилл

❓ Как работает полифилл для Object.hasOwn()? Почему внутри используется Object.prototype.hasOwnProperty.call()?

js
if (!Object.hasOwn) {
  Object.defineProperty(Object, 'hasOwn', {
    value: function(obj, prop) {
      if (obj == null) {
        throw new TypeError('Cannot convert undefined or null to object');
      }
      return Object.prototype.hasOwnProperty.call(obj, prop);
    },
    configurable: true,
    enumerable: false,
    writable: true
  });
}