| API | Own | Inherited | Non-enumerable | Symbols |
|---|---|---|---|---|
for (k in o) |
yes | yes (enumerable) | no | no |
Object.keys / values / entries |
yes | no | no | no |
Object.getOwnPropertyNames |
yes | no | yes | no |
Object.getOwnPropertySymbols |
yes | no | yes | symbols only |
Reflect.ownKeys |
yes | no | yes | yes |
All own-key variants use the order from the previous question (indices, strings, symbols), and Object.keys, values and entries are defined by EnumerableOwnProperties: take [[OwnPropertyKeys]], keep the string keys whose [[Enumerable]] is true, and call Get for values.
for…in walks the prototype chain. It never repeats a name, and a name on the object shadows the same name higher up even when the own property is non-enumerable. Deleted properties that haven't been visited are skipped; properties added during the loop aren't guaranteed to be visited. Array keys arrive as strings, and anything added to Array.prototype appears.
Ownership check. Use Object.hasOwn(obj, key) rather than obj.hasOwnProperty(key): it works on Object.create(null) objects and objects that define their own hasOwnProperty. 'k' in obj also searches the prototype chain, and propertyIsEnumerable tests own and enumerable at once.
Primitives. Object.keys('ab') coerces with ToObject and returns ['0', '1']; null and undefined throw a TypeError, while for…in over them simply does nothing.
Prefer Object.keys/entries for data, for…of over Object.entries(o) for loops, and for…in only when you want inherited keys.