Какие способы есть для итерации по объекту в JavaScript кроме for-in
Что проверяют
Знаете ли вы, какие именно ключи попадают в каждый способ: собственные или унаследованные, перечисляемые или нет, строки или символы. Следом часто спрашивают, почему for...of по обычному объекту падает и в каком порядке идут ключи.
Как отвечать
Object.keys(),Object.values(),Object.entries(): только собственные перечисляемые свойства со строковыми ключами. Самый частый вариант:for (const [key, value] of Object.entries(obj));Object.getOwnPropertyNames(): собственные строковые ключи, включая неперечисляемые;Object.getOwnPropertySymbols(): собственные ключи-символы;Reflect.ownKeys(): все собственные ключи сразу, строки и символы, перечисляемые и нет;Object.fromEntries(): не обход, а пара кentries(). Позволяет пропустить объект черезmapиfilterи собрать обратно.
Отличие от for...in: он обходит и перечисляемые свойства из цепочки прототипов, поэтому внутри нужна проверка, сейчас это Object.hasOwn() из ES2022 вместо hasOwnProperty. Методы Object.* прототип не трогают.
Про порядок: сначала целочисленные ключи по возрастанию, затем строковые в порядке добавления, символы в конце. Если важен порядок вставки для любых ключей или объект часто меняется, лучше взять Map: он итерируемый, ключи любого типа, size без подсчёта.
Обычный объект не итерируемый, у него нет Symbol.iterator. Можно добавить свой через генератор, *[Symbol.iterator]() { yield* Object.entries(this) }, но это редко оправдано.
Где спотыкаются
Пишут for (const x of obj) или obj.forEach(...) и получают TypeError. Итерировать надо результат Object.entries() или Object.keys().
Ожидают, что ключи "10", "2", "b" выйдут в порядке добавления. Целочисленные ключи всегда идут первыми и по возрастанию, на этом ломаются словари с id в роли ключа.
Используют forEach с асинхронным колбэком и ждут, что обход дождётся каждого await. Не дождётся: для последовательной обработки нужен for...of, для параллельной Promise.all с map.