> Чем собственные свойства отличаются от унаследованных в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: EvApps

Стек: JavaScript

> Пример ответа

Короткий ответ

Собственные свойства принадлежат самому объекту и определяются через Object.hasOwnProperty() или Object.getOwnPropertyNames(). Унаследованные свойства приходят через prototype chain - они доступны для чтения, но не принадлежат объекту напрямую. Ключевое различие: собственные свойства перечисляются в for...in и учитываются в Object.keys(), а унаследованные - нет. При присваивании значения создаётся собственное свойство, которое перекрывает унаследованное.

Подробное объяснение

Каждый объект в JavaScript имеет внутреннюю ссылку [[Prototype]], которая образует цепочку прототипов. Когда обращаешься к свойству, движок сначала проверяет собственные свойства объекта, затем поднимается по цепочке прототипов до Object.prototype и дальше до null.

Собственные свойства - это те, что определены непосредственно на объекте через литерал, конструктор, Object.defineProperty или присваивание. Они хранятся в внутреннем слоте [[OwnPropertyKeys]] объекта.

Унаследованные свойства - это свойства, найденные в прототипах. Они не создают новых записей в объекте, а лишь доступны через механизм prototype lookup. Важно: унаследованные свойства нельзя перечислить через Object.keys() или Object.getOwnPropertyNames(), но они видны в for...in (если не переопределены).

Различие критично для:

  • сериализации (JSON.stringify работает только с собственными свойствами)
  • копирования объектов (shallow copy через spread или Object.assign не захватывает унаследованное)
  • проверки наличия свойства (in оператор учитывает цепочку, hasOwnProperty - нет)
  • производительности: доступ к унаследованному свойству медленнее, так как требует обхода цепочки

При присваивании значения унаследованному свойству создаётся собственное свойство с тем же именем - это называется shadowing. Исключение - свойства с сеттером в прототипе: тогда вызывается сеттер, а не создаётся собственное свойство.

На практике

В реальной разработке различие важно при работе с классами и наследованием. Методы обычно живут в прототипе, а данные - как собственные свойства. Это экономит память: методы не дублируются для каждого экземпляра.

При работе с фреймворками и библиотеками часто встречается паттерн проверки Object.prototype.hasOwnProperty.call(obj, key) - это защита от переопределённого hasOwnProperty на самом объекте.

Для безопасного копирования объектов с учётом только собственных свойств используют Object.entries() или Object.keys() в комбинации с Object.fromEntries().

При создании объектов через Object.create(null) объект не имеет прототипа, поэтому у него нет унаследованных свойств вообще - это полезно для словарей, где ключи могут быть __proto__ или toString.

Пример кода

JAVASCRIPT
const parent = {
inherited: 'from parent',
greet() {
return 'Hello';
}
};
const child = Object.create(parent);
child.own = 'own property';
console.log(child.inherited); // 'from parent' - унаследованное
console.log(child.own); // 'own property' - собственное
console.log(Object.keys(child)); // ['own'] - только собственные
console.log(child.hasOwnProperty('inherited')); // false
console.log(child.hasOwnProperty('own')); // true
console.log('inherited' in child); // true - оператор in видит цепочку
// Shadowing: создаём собственное свойство с тем же именем
child.inherited = 'shadowed';
console.log(child.hasOwnProperty('inherited')); // true
console.log(parent.inherited); // 'from parent' - прототип не изменился
// Проверка через Object.getOwnPropertyNames
console.log(Object.getOwnPropertyNames(child)); // ['own', 'inherited']
// for...in перечисляет и унаследованные перечислимые свойства
for (const key in child) {
console.log(key); // 'own', 'inherited', 'greet'
}

Как отвечать на собеседовании

Начни с чёткого определения: собственные свойства - это свойства, которые принадлежат объекту напрямую, унаследованные - те, что доступны через prototype chain. Подчеркни разницу в перечислении: Object.keys() и hasOwnProperty работают только с собственными, for...in и in - с унаследованными тоже.

Покажи понимание механизма: при обращении к свойству движок сначала ищет в собственных, потом поднимается по цепочке. Упомяни shadowing - присваивание создаёт собственное свойство, перекрывающее унаследованное.

Для senior-позиции добавь нюансы: разницу в производительности доступа, поведение с сеттерами в прототипе, особенности Object.create(null), важность для сериализации и копирования. Если спросят про классы - объясни, что методы в class автоматически попадают в прототип, а поля - в собственные свойства.

Что проверяет интервьюер

Интервьюер оценивает:

  • понимание prototype chain как фундаментального механизма JavaScript
  • умение различать способы проверки свойств: in, hasOwnProperty, Object.keys(), Object.getOwnPropertyNames()
  • знание практических следствий: сериализация, копирование, перечисление
  • способность объяснить shadowing и его последствия
  • для senior - понимание внутренних механизмов и edge cases

Вопрос часто служит входной точкой для более глубокого обсуждения: наследование, Object.create, классы, производительность, мемоизация через прототипы.

Типичные ошибки

  • путаница между in и hasOwnProperty - первое видит унаследованные, второе нет
  • утверждение, что for...in перечисляет только собственные свойства - это неверно
  • игнорирование shadowing: кандидат думает, что присваивание меняет прототип - это не так
  • забывают про Object.create(null) как способ полностью исключить унаследованные свойства
  • не учитывают, что JSON.stringify сериализует только собственные перечислимые свойства
  • используют obj.hasOwnProperty(key) без защиты от переопределения - правильнее Object.prototype.hasOwnProperty.call(obj, key)

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью