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 keys method that returns an iterator:
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/>};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...of loops.
Using for...in can achieve similar results, but for...of works 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...of loops.
1. Traversing array‑like structures
When a for...of loop runs, it looks for the Symbol.iterator property. Any object with this property is considered iterable.
2. Getting an object's enumerable properties
Object.keys, Object.values, and Object.entries return only the object's own enumerable properties. Plain objects are not iterable because they lack Symbol.iterator, so for...of throws a TypeError.
3. Using for...of
Introduced in ES6, for...of offers concise syntax, supports break, continue, and return, and works across all iterable structures.
To make a plain object iterable, we can implement Symbol.iterator as shown:
// 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/>}This enables for...of to iterate over plain objects just like arrays.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
