Как в цикле for-in в JavaScript вывести только собственные ключи объекта без ключей прототипа
Что проверяют
Понимаете ли вы прототипное наследование и чем собственные свойства отличаются от унаследованных. Следом обычно спрашивают, чем заменить for...in и почему линтер ругается на obj.hasOwnProperty(key).
Как отвечать
for...in обходит все перечисляемые строковые ключи объекта, включая унаследованные по цепочке прототипов, и пропускает символы. Чтобы оставить только свои, внутри цикла ставят проверку:
Object.hasOwn(obj, key): современный вариант из ES2022, работает во всех актуальных браузерах и Node.js. Его и стоит называть первым;Object.prototype.hasOwnProperty.call(obj, key): то же самое для старых окружений. Вызов черезcallнужен, потому что у самого объекта этого метода может не быть;obj.hasOwnProperty(key): работает на обычных объектах, но падает сTypeErrorна объекте изObject.create(null)и врёт, если в данных есть собственный ключhasOwnProperty, например пришедший из JSON. Поэтому правило ESLintno-prototype-builtinsего запрещает.
Сильный ответ продолжается мыслью, что без for...in часто лучше вовсе обойтись:
Object.keys(obj),Object.entries(obj): только собственные перечисляемые строковые ключи, проверка не нужна, и сразу доступны методы массивов;Object.getOwnPropertyNames(obj): добавит неперечисляемые собственные свойства;Reflect.ownKeys(obj): все собственные ключи, включая символы.
Можно назвать и порядок ключей: сначала целочисленные по возрастанию, потом строковые в порядке добавления. Для словаря с произвольными ключами уместнее Map.
Где спотыкаются
Путают оператор in с проверкой собственного свойства: "key" in obj тоже смотрит всю цепочку прототипов.
Обходят через for...in массив. Индексы приходят строками, а вместе с ними любые перечисляемые свойства, которые сторонний код добавил в прототип.
Считают, что Object.keys вернёт символы или неперечисляемые свойства, и удивляются, когда их нет в результате.