Master JavaScript Object Traversal: keys(), values(), entries() & Custom Iterators
This article explains the differences between Object.keys/values/entries and their array counterparts, shows how to use them for object property iteration, demonstrates creating custom iterator methods for plain objects, and compares iterator results with array returns, providing code examples and visual diagrams.
Background
The story begins with a confusion when traversing arrays and object properties: whether to use obj.keys(), obj.values(), obj.entries() or the static methods Object.keys(obj), Object.values(obj), Object.entries(obj).
keys(), values(), entries() traversal methods
Native object structures do not support obj.keys(), obj.values(), or obj.entries(); only arrays, Map, Set, etc., have those methods. However, Object.keys(obj), Object.values(obj), and Object.entries(obj) can retrieve an object's own enumerable property names, values, or [key, value] pairs as arrays.
There are two versions of these methods:
ES5‑ES2017 introduced Object.keys, Object.values, and Object.entries, returning arrays of an object's own enumerable keys, values, or entries, which can be iterated with
for...of.
ES6 added
entries(),
keys(), and
values()for array‑like structures, returning an iterator object.
Key differences:
The call syntax differs: static methods vs. prototype methods.
The static methods return a true array, while the prototype methods return an iterator.
Q1: Object.keys, Object.values and Object.entries
These static methods operate on the Object prototype's constructor. The following diagram shows their placement in the prototype chain for a plain object:
For an array, the same three methods can be found both on the array prototype and higher‑level prototype constructors:
Thus,
Object.keys(arr)calls the method defined on the top‑level prototype, while
arr.keys()invokes the array’s own prototype method.
Q2: Enabling obj.keys(), obj.values() and obj.entries() on a plain object
By adding a custom iterator to an object, we can mimic array‑like traversal. The following code creates an object with its own
keysmethod that returns an iterator:
<code>let objE = {<br/> data: ['hello', 'world'],<br/> keys: function() {<br/> const self = this;<br/> return {<br/> [Symbol.iterator]() {<br/> let index = 0;<br/> return {<br/> next() {<br/> if (index < self.data.length) {<br/> return { value: self.data[index++], done: false };<br/> }<br/> return { value: undefined, done: true };<br/> }<br/> };<br/> }<br/> };<br/> }<br/>};</code>The object now supports
Object.values(data)and its own iterator. The diagram below illustrates the added
Symbol.iterator:
Q3: Return differences – iterator vs. array
The static methods return arrays containing only the object's own enumerable properties. The iterator methods return an iterator object that can be consumed by
for...ofloops.
Using
for...incan achieve similar results, but
for...ofworks directly with iterators.
Iterator Overview
Four main collection‑like structures—Array, Object, Map, and Set—can be traversed via iterators. Native structures that implement the Iterator interface include Array, Map, Set, String, TypedArray, arguments, and NodeList.
Iterators provide a unified access mechanism for all iterable data structures, enabling
for...ofloops.
1. Traversing array‑like structures
When a
for...ofloop runs, it looks for the
Symbol.iteratorproperty. Any object with this property is considered iterable.
2. Getting an object's enumerable properties
Object.keys,
Object.values, and
Object.entriesreturn only the object's own enumerable properties. Plain objects are not iterable because they lack
Symbol.iterator, so
for...ofthrows a TypeError.
3. Using for...of
Introduced in ES6,
for...ofoffers concise syntax, supports
break,
continue, and
return, and works across all iterable structures.
To make a plain object iterable, we can implement
Symbol.iteratoras shown:
<code>// plain object<br/>const obj = {<br/> foo: 'value1',<br/> bar: 'value2',<br/> [Symbol.iterator]() {<br/> const keys = Object.keys(obj);<br/> let index = 0;<br/> return {<br/> next: () => {<br/> if (index < keys.length) {<br/> return { value: this[keys[index++]], done: false };<br/> } else {<br/> return { value: undefined, done: true };<br/> }<br/> }<br/> };<br/> }<br/>};<br/>for (const value of obj) {<br/> console.log(value); // value1 value2<br/>}</code>This enables
for...ofto iterate over plain objects just like arrays.
WeDoctor Frontend Technology
Official WeDoctor Group frontend public account, sharing original tech articles, events, job postings, and occasional daily updates from our tech team.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.