JavaScript example

How to loop through an object in JavaScript

6 min read Updated Sep 2026 Runs in an isolated runtime
Quick answer

Use for...of over Object.entries() and destructure each pair: for (const [key, value] of Object.entries(obj)). It visits the object's own properties only, and break and continue work. Use Object.keys(obj) or Object.values(obj) when you need only one side.

A plain object is not iterable, so for...of can't loop over it directly. The Object.keys, Object.values and Object.entries functions turn it into an array you can loop over. The older for...in loop still works, but it also walks inherited properties, which is why it usually comes with a guard. This page covers all of those, the order the keys come back in (it isn't always the order you wrote them), and how to map or filter an object. Each example runs on this page: hit Run, then edit the code and run it again.

1for...of with Object.entries()Recommended

Object.entries(obj) returns an array of [key, value] pairs for the object's own enumerable string keys. Loop over it with for...of and destructure each pair in the loop head, so you never write obj[key]. When the values are themselves objects, you can destructure those too, in the same line.

entries.js

Output

Prints apple: 12 and cherry: 7 (banana is skipped by the continue), then [ [ 'apple', 12 ], [ 'banana', 0 ], [ 'cherry', 7 ] ], then ada admin active and linus dev inactive. The array is built up front, so adding or deleting keys on the object inside the loop can't change which entries the loop visits.

2Object.keys() and Object.values()

When you only need one side, Object.keys gives the keys and Object.values gives the values, both as ordinary arrays. That means you can skip the loop entirely and use reduce, filter, some or .length. forEach also works on the result, but unlike for...of it has no way to stop early.

keys-values.js

Output

Prints [ 'apple', 'banana', 'cherry' ], [ 12, 0, 7 ], units: 19 kinds: 3 and [ 'banana' ], then one line per fruit. Object.keys(obj).length is the standard way to count an object's properties; there is no obj.length. Inside forEach, return only ends the current callback, so if you need to stop at the first match, use for...of or .some()/.find() instead.

3for...in, inherited keys, and for...in vs for...of

for...in is the original way to enumerate an object, and it needs no helper. The catch is that it visits every enumerable key on the prototype chain, not just the object's own ones. That is why older code wraps its body in a hasOwnProperty check; today you write Object.hasOwn(obj, key). for...of is a different loop entirely: it works on iterables (arrays, strings, Map, Set), and a plain object isn't one.

for-in.js

Output

The first loop prints fontSize, tabs and then the inherited theme. The guarded loop prints own: fontSize 14 and own: tabs true, and Object.keys agrees: [ 'fontSize', 'tabs' ]. for...of on the object throws TypeError: settings is not iterable. On the array, for...in prints string 0 and string 1 (keys are always strings) while for...of prints js and node. The short rule: for...in walks the keys of any object, for...of walks the values of an iterable. Object.hasOwn replaces obj.hasOwnProperty(key) because it also works on objects made with Object.create(null), which have no such method.

4Mapping and filtering an object with Object.fromEntries()

Objects have no .map() or .filter(). The idiom is a round trip: Object.entries to get an array of pairs, the array method you need, then Object.fromEntries to turn the pairs back into an object. Because you return a [key, value] pair, the same pattern can change the values, drop entries, or rename and swap keys. The result is always a new object; the original is left alone.

map-object.js

Output

Prints { apple: '1.20', bread: '3.50', milk: '0.99' }, then { apple: 120, milk: 99 }, then { '99': 'milk', '120': 'apple', '350': 'bread' } and { a: 10, b: 20 }. Look at the swapped object: the numbers became string keys, and '99' moved to the front even though milk was added last. That is the key-ordering rule in section 6 at work. Object.fromEntries accepts any iterable of pairs, so it also turns a Map into a plain object.

5Which should you use?

MethodGives youInherited keysBest for
for (const [k, v] of Object.entries(obj))Key and valueSkippedThe default loop; break and continue work
Object.keys(obj) / Object.values(obj)One side, as an arraySkippedCounting, summing, filter/some/reduce
Object.fromEntries(entries.map(…))A new objectSkippedMapping or filtering an object
Object.entries(obj).forEach(…)Key and valueSkippedSide effects; cannot break early
for (const key in obj)Keys onlyIncludedOlder code; guard with Object.hasOwn
for (const [k, v] of map)Key and valuen/aMap: any key type, insertion order

6Key order, and when to use a Map instead

Every loop on this page returns keys in the same, fully specified order, and it is not simply insertion order. Integer-like keys ("0", "2", "10") come first, in ascending numeric order. Every other string key follows in the order it was added. Symbol keys are skipped entirely. If you need insertion order for every key, or keys that aren't strings, use a Map: it remembers insertion order and is iterable, so for...of loops over it directly.

key-order.js

Output

The first line prints 0, 2, 10, b, a, -1, 1.5, z: the three integer keys in numeric order, then b, a, -1, 1.5 and z in the order they were added, because "-1" and "1.5" are not integer-like. The IDs come back as [ 'ada', 'bob', 'carol' ], sorted by ID. The Map prints 30 carol, 4 ada, 12 bob in insertion order, then [ 30, 4, 12 ] 3: its keys stay numbers and .size counts them. The last line is [ 'name' ] [ 'name', Symbol(id) ]. A Map also has .keys(), .values(), .entries() and .forEach((value, key) => …); note that forEach passes the value first.

Frequently asked questions

What is the difference between for...in and for...of in JavaScript?

for...in loops over the enumerable string keys of any object, including keys inherited through the prototype chain. for...of loops over the values of an iterable: arrays, strings, Map, Set and generators. A plain object is not iterable, so for (const x of obj) throws TypeError: obj is not iterable; loop over Object.entries(obj) instead. On an array, for...in gives the indexes as strings, which is why it is the wrong loop for arrays.

How do I map over an object in JavaScript?

Objects have no map method, so convert to entries, map them, and convert back: Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, fn(value)])). The callback returns a [key, value] pair, so it can change keys as well as values, and swapping map for filter drops entries. The result is a new object and the original is unchanged.

In what order does JavaScript loop over object keys?

Integer-like keys such as "0", "2" and "10" come first in ascending numeric order, then all other string keys in the order they were added. Symbol keys are left out of for...in, Object.keys, Object.values and Object.entries. The order is part of the language specification, so it is the same in every modern engine. If you need pure insertion order, or number keys that stay numbers, use a Map.

Do I still need hasOwnProperty in a for...in loop?

Only if you use for...in, which also visits inherited enumerable properties. The modern guard is if (!Object.hasOwn(obj, key)) continue;, which works even on objects created with Object.create(null) that have no hasOwnProperty method. Simpler still, loop over Object.keys(obj) or Object.entries(obj), which only return own properties, and skip the guard.

Can I add or delete properties while looping through an object?

With Object.keys or Object.entries it is safe, because they return an array snapshot before the loop starts, so changes to the object do not affect which entries you visit. With for...in, a property deleted before the loop reaches it is not visited, and a property added during the loop may or may not be visited. If you need to change keys, build a new object with Object.fromEntries instead.

How do I break out of a loop over an object?

Use for...of over Object.entries(obj) (or for...in) and call break. forEach cannot be stopped: return inside the callback only skips to the next entry. If you are looking for the first matching entry, Object.entries(obj).find(([key, value]) => ...) returns it and stops at the first match.

Run it yourself

Open any of these in the full JavaScript editor: tweak, run, and share.

JavaScript playground