Object.hasOwn вместо hasOwnProperty

10 сентября 2026
Автор

Антон Ларичев

Что такое собственное свойство объекта

В JavaScript каждый объект может иметь два типа свойств: собственные (own) и унаследованные через цепочку прототипов. Собственные свойства — это те, что были определены непосредственно на самом объекте, а не получены от его прототипа.

const user = {
  name: 'Alice',
  age: 30
};

console.log('name' in user);       // true — собственное
console.log('toString' in user);   // true — унаследованное от Object.prototype

Оператор in проверяет наличие свойства по всей цепочке прототипов, поэтому для проверки именно собственного свойства традиционно использовали hasOwnProperty.

Курс по теме

Курс JavaScript с нуля

40 000+ студентов · рейтинг 4.8 · гарантия возврата 30 дней

Проблемы hasOwnProperty

Метод hasOwnProperty существует в JavaScript с самого начала и знаком каждому разработчику:

const user = {
  name: 'Alice',
  age: 30
};

console.log(user.hasOwnProperty('name'));     // true
console.log(user.hasOwnProperty('toString')); // false

Однако у него есть несколько серьёзных проблем, которые могут привести к труднообнаруживаемым багам.

Проблема 1: метод можно переопределить

hasOwnProperty — это обычный метод, доступный через прототип. Это означает, что его можно перезаписать на конкретном объекте:

const obj = {
  hasOwnProperty: function() {
    return false; // всегда возвращает false
  },
  name: 'Alice'
};

console.log(obj.hasOwnProperty('name')); // false — неверный результат!

На практике это случается, когда объект создаётся из данных, пришедших извне: например, из JSON или пользовательского ввода. Если входные данные содержат ключ hasOwnProperty, объект получит свойство, которое затенит метод прототипа.

// Данные из внешнего источника
const parsed = JSON.parse('{"hasOwnProperty": true, "value": 42}');

// Падает с ошибкой: TypeError: parsed.hasOwnProperty is not a function
console.log(parsed.hasOwnProperty('value'));

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

Объекты, созданные через Object.create(null), не имеют прототипа. Они не наследуют ни один метод из Object.prototype, включая hasOwnProperty.

const dict = Object.create(null);
dict.name = 'Alice';

// Ошибка: TypeError: dict.hasOwnProperty is not a function
console.log(dict.hasOwnProperty('name'));

Такие объекты часто используют именно как «чистые» словари, чтобы избежать конфликтов с унаследованными свойствами. Это популярный паттерн для хранения произвольных ключей.

Проблема 3: правило линтера

ESLint имеет правило no-prototype-builtins, которое запрещает вызывать методы Object.prototype напрямую через экземпляр. Это правило включено в конфигурации eslint:recommended. Поэтому во многих командах obj.hasOwnProperty() помечается как ошибка линтера.

Обходной путь, который использовали до ES2022:

// Безопасный, но многословный способ
Object.prototype.hasOwnProperty.call(obj, 'name');

// Или с сохранением ссылки
const hasOwn = Object.prototype.hasOwnProperty;
console.log(hasOwn.call(obj, 'name')); // true

Это работает, но синтаксически громоздко и неочевидно для читателя кода.

Object.hasOwn — современное решение

В ES2022 появился статический метод Object.hasOwn, который решает все описанные проблемы. Он принимает объект и имя свойства, возвращая true, если свойство является собственным.

const user = {
  name: 'Alice',
  age: 30
};

console.log(Object.hasOwn(user, 'name'));     // true
console.log(Object.hasOwn(user, 'toString')); // false

Синтаксис

Object.hasOwn(obj, propKey)
  • obj — объект, который проверяется. Если передать не-объект, метод выбросит TypeError.
  • propKey — строка или символ, имя проверяемого свойства.

Метод возвращает boolean.

Сравнение с hasOwnProperty

Устойчивость к переопределению

Object.hasOwn — статический метод самого конструктора Object, а не метод прототипа. Его невозможно случайно переопределить на уровне конкретного объекта:

const obj = {
  hasOwnProperty: function() {
    return false;
  },
  name: 'Alice'
};

// hasOwnProperty переопределён — даёт неверный результат
console.log(obj.hasOwnProperty('name')); // false

// Object.hasOwn не зависит от объекта — всегда работает правильно
console.log(Object.hasOwn(obj, 'name')); // true

Работа с объектами без прототипа

const dict = Object.create(null);
dict.key = 'value';
dict.count = 42;

// hasOwnProperty не доступен — объект без прототипа
// dict.hasOwnProperty('key') => TypeError

// Object.hasOwn работает без проблем
console.log(Object.hasOwn(dict, 'key'));   // true
console.log(Object.hasOwn(dict, 'other')); // false

Итерация по ключам

Распространённый паттерн — перебрать только собственные перечислимые свойства объекта, исключив унаследованные. Раньше код выглядел так:

const settings = { theme: 'dark', lang: 'ru', debug: false };

// Старый способ
for (const key in settings) {
  if (Object.prototype.hasOwnProperty.call(settings, key)) {
    console.log(key, settings[key]);
  }
}

С Object.hasOwn код становится чище:

const settings = { theme: 'dark', lang: 'ru', debug: false };

for (const key in settings) {
  if (Object.hasOwn(settings, key)) {
    console.log(key, settings[key]);
  }
}
// theme dark
// lang ru
// debug false

Хотя для этой задачи лучше подходит Object.keys(), который и так возвращает только собственные перечислимые свойства, комбинация for...in + Object.hasOwn удобна, когда нужно обойти также неперечислимые свойства или свойства-символы с дополнительной фильтрацией.

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

Безопасная проверка наличия ключа в конфигурации

function applyConfig(config) {
  const defaults = { timeout: 3000, retries: 3, debug: false };

  for (const key in defaults) {
    if (!Object.hasOwn(config, key)) {
      config[key] = defaults[key];
    }
  }

  return config;
}

const result = applyConfig({ timeout: 5000 });
console.log(result);
// { timeout: 5000, retries: 3, debug: false }

Валидация данных из внешнего источника

function validateUser(data) {
  const requiredFields = ['name', 'email', 'age'];

  for (const field of requiredFields) {
    if (!Object.hasOwn(data, field)) {
      throw new Error(`Отсутствует обязательное поле: ${field}`);
    }
  }

  return true;
}

// Данные из API — могут содержать любые ключи
const userData = JSON.parse('{"name":"Bob","email":"bob@example.com","age":25}');

try {
  validateUser(userData);
  console.log('Данные валидны');
} catch (e) {
  console.error(e.message);
}

Работа с Map-подобными объектами

// Объект как словарь без прototipe-загрязнения
function createRegistry() {
  return Object.create(null);
}

const registry = createRegistry();
registry['user:1'] = { name: 'Alice' };
registry['user:2'] = { name: 'Bob' };

function getEntry(registry, id) {
  if (Object.hasOwn(registry, id)) {
    return registry[id];
  }
  return null;
}

console.log(getEntry(registry, 'user:1')); // { name: 'Alice' }
console.log(getEntry(registry, 'user:3')); // null

Проверка свойств-символов

Object.hasOwn работает не только со строками, но и с символами:

const ID = Symbol('id');

const entity = {
  [ID]: 12345,
  name: 'Product'
};

console.log(Object.hasOwn(entity, ID));     // true
console.log(Object.hasOwn(entity, 'name')); // true
console.log(Object.hasOwn(entity, Symbol('id'))); // false — разные символы

Поддержка браузеров и полифилл

Object.hasOwn введён в ES2022 и поддерживается во всех современных браузерах начиная с 2022 года: Chrome 93+, Firefox 92+, Safari 15.4+, Node.js 16.9+.

Если нужна поддержка более старых окружений, полифилл прост:

if (!Object.hasOwn) {
  Object.hasOwn = function(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
  };
}

Этот полифилл можно добавить один раз на уровне точки входа приложения. Babel и core-js начиная с версии 3.16 также включают его автоматически при соответствующих настройках targets.

Когда использовать Object.hasOwn, а когда — альтернативы

Выбор метода зависит от задачи:

Задача Рекомендуемый подход
Проверить собственное свойство Object.hasOwn(obj, key)
Перечислить собственные перечислимые свойства Object.keys(obj) или Object.entries(obj)
Проверить наличие свойства в цепочке прототипов key in obj
Перечислить все собственные свойства (включая неперечислимые) Object.getOwnPropertyNames(obj)
Перечислить собственные символьные свойства Object.getOwnPropertySymbols(obj)

Для большинства случаев, когда нужно убедиться, что ключ задан непосредственно на объекте, а не унаследован — используйте Object.hasOwn.

Переход с hasOwnProperty на Object.hasOwn

Если в существующем коде есть вызовы hasOwnProperty, их можно заменить по следующим правилам:

// Было
obj.hasOwnProperty(key)

// Стало
Object.hasOwn(obj, key)

// Было (через call — защита от переопределения)
Object.prototype.hasOwnProperty.call(obj, key)

// Стало
Object.hasOwn(obj, key)

Замена механическая и безопасна в любом из перечисленных вариантов — поведение идентично для обычных объектов, но Object.hasOwn корректно работает ещё и с объектами без прототипа.

ESLint-правило prefer-object-has-own (доступно начиная с ESLint 8.5) автоматически помечает устаревшие вызовы hasOwnProperty и предлагает автофикс:

{
  "rules": {
    "prefer-object-has-own": "error"
  }
}

Итог

Object.hasOwn — это безопасная, лаконичная и рекомендуемая замена hasOwnProperty. Он не зависит от прототипа объекта, не может быть случайно переопределён и читается естественно. Используйте его во всём новом коде и постепенно мигрируйте существующий — ESLint-правило prefer-object-has-own упростит поиск мест для замены.

Чтобы глубже разобраться с объектами, прототипами и современными возможностями JavaScript, записывайтесь на курс: JavaScript для профессионалов на PurpleSchool

Стрелочка влевоObject.keys, Object.values, Object.entries в JavaScriptObject.groupBy в JavaScript — группировка элементовСтрелочка вправо

Постройте личный план изучения Javascript до уровня Middle — бесплатно!

Javascript — часть карты развития Frontend

  • step100+ шагов развития
  • lessons30 бесплатных лекций
  • lessons300 бонусных рублей на счет

Бесплатные лекции

Все гайды по Javascript

Как работает метод trim() - JavaScriptКак работает метод toUpperCase() - JavaScriptКак работает метод toLowerCase() - JavaScriptКак работает метод substring() - JavaScriptКак работает метод startsWith() - JavaScriptКак работает метод split() - JavaScriptКак работает метод slice() - JavaScriptКак работает метод search() - JavaScriptКак работает метод replaceAll() - JavaScriptКак работает метод replace() - JavaScriptКак работает метод repeat() - JavaScriptКак работает метод padStart() - JavaScriptКак работает метод padEnd() - JavaScriptКак работает метод matchAll() - JavaScriptКак работает метод match() - JavaScriptКак работает метод localeCompare() - JavaScriptКак работает свойство length - JavaScriptКак работает метод lastIndexOf() - JavaScriptКак работает метод indexOf() - JavaScriptКак работает метод includes() - JavaScriptКак работает метод fromCodePoint() - JavaScriptКак работает метод endsWith() - JavaScriptКак работает метод fromCharCode() - JavaScriptКак работает метод concat() - JavaScriptКак работает метод codePointAt() - JavaScriptКак работает метод charCodeAt() - JavaScriptКак работает метод charAt() - JavaScript
Итератор в JavaScript
try...catch в JavaScriptError в JavaScript
Событие wheel в JavaScriptСобытие unload в JavaScriptСобытие touch в JavaScriptСобытие submit в JavaScriptСобытие scroll в JavaScriptСобытие reset в JavaScriptМетод .preventDefault() в JavaScriptСобытие mouseover в JavaScriptСобытие mouseout в JavaScriptСобытие load в JavaScriptСобытие keyup в JavaScriptСобытие keydown в JavaScriptСобытие invalid в JavaScriptСобытие input в JavaScriptСобытийная модель Event в JavaScriptОбъект события Event в JavaScriptСобытие DOMContentLoaded в JavaScriptСобытие dblclick в JavaScriptСобытие click в JavaScriptСобытие change в JavaScriptBroadcast Channel API в JavaScriptJavaScript BroadcastChannel — межвкладочное взаимодействиеСобытие beforeunload в JavaScript
Error cause — цепочки ошибок в JavaScript
Методы массивов toSorted, toReversed и withКак работает метод some() - JavaScriptКак работает метод reverse() - JavaScriptКак работает метод reduce() - JavaScriptКак работает метод map() - JavaScriptКак работает метод isArray() - JavaScriptКак работает метод indexOf() - JavaScriptКак работает метод includes() - JavaScriptКак работает метод from() - JavaScriptКак работает метод forEach() - JavaScriptКак работает метод flatMap() - JavaScriptКак работает метод flat() - JavaScriptКак работает метод findIndex() - JavaScriptКак работает метод find() - JavaScriptКак работает метод filter() - JavaScriptКак работает метод every() - JavaScriptМассивы в JavaScriptArray.at, findLast, findLastIndex — новые методы массивов
Открыть базу знаний

Лучшие курсы по теме

изображение курса

Основы JavaScript

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее

Отправить комментарий