Get keys of object javascript

How to Get Keys of Object Javascript in 2026

By Zest | | 21 min read
How to Get Keys of Object Javascript in 2026

If you’ve been working with JavaScript for any length of time, you know how often you need to peek inside an object and see what’s there. Getting a list of an object’s keys is a fundamental task, whether you’re looping over data from an API, checking form inputs, or just figuring out an object’s structure.

But as with many things in JavaScript, there’s more than one way to do it. The method you choose can have a big impact, especially when you start dealing with more complex objects. This guide will walk you through the different tools for the job, so you can write cleaner and more predictable code.

We’ll look at the subtle but crucial differences between the various ways to get object keys, covering things like:

  • How each method treats properties inherited from a prototype.
  • Whether or not they include modern Symbol properties.
  • What “enumerable” means and why it’s a big deal.

This handy decision tree gives you a quick visual for picking the right tool. Think of it as a cheat sheet for your next project.

A JavaScript object keys decision tree diagram showing methods to retrieve all, enumerable, or prototype keys.

As you can see, the best method really depends on what you’re trying to accomplish—do you need absolutely everything, or just the straightforward, everyday properties?

Object.keys(): The Go-To Method

For most day-to-day coding, your best friend is Object.keys(). Introduced back in ES5, it has become the standard for a reason. It gives you an array of an object’s own enumerable string properties. It’s simple, predictable, and does exactly what most developers need, most of the time.

In my experience, Object.keys() (along with its cousins Object.values() and Object.entries()) shows up in nearly every production codebase I’ve seen. Its widespread use is a testament to its reliability. For a deeper dive into how JavaScript handles these structures, check out our guide on JavaScript data types.

Object.keys() is incredibly fast because it works with JavaScript’s internal hash maps. This efficiency is why it’s my default choice for iterating over an object’s properties. It’s performant and gets the job done without any surprises.

This guide will start with Object.keys() and then branch out into the other methods you’ll need for more complex situations. Let’s get started.

Using Object.keys for Everyday Tasks

For most of your day-to-day coding in JavaScript, Object.keys() is going to be your go-to. Think of it as the reliable workhorse you’ll reach for 90% of the time you need to grab an object’s keys.

Its behavior is wonderfully simple: it gives you an array of an object’s very own enumerable, string-keyed properties. This makes it perfect for common tasks like looping over data from an API response or pulling values from a form submission. The array it hands back works seamlessly with all the standard array methods you know and love, like .forEach(), .map(), and .filter().

Understanding “Own” and “Enumerable” Properties

To really get why Object.keys() is so predictable, you need to understand two key terms: “own” and “enumerable”.

An “own” property is one that lives directly on the object itself, not something it inherited from its prototype. An “enumerable” property is simply one that’s flagged to show up in loops. When you create a plain object, any properties you add are enumerable by default.

Let’s look at a classic user profile object to see this in action:

const userProfile = { id: 123, username: ‘dev_user’, email: ‘user@example.com’ };

// Now, let’s add a non-enumerable property. Object.defineProperty(userProfile, ‘internalId’, { value: ‘xyz-987’, enumerable: false });

const keys = Object.keys(userProfile);

console.log(keys); // Output: [‘id’, ‘username’, ‘email’]

See how internalId is missing? We explicitly told JavaScript that this property isn’t enumerable, so Object.keys() respects that and leaves it out. This is a feature, not a bug—it’s a great way to hide internal properties from regular iteration.

Why It Ignores Prototypes and Symbols

Another crucial design choice is that Object.keys() intentionally ignores properties from an object’s prototype chain and any properties that use Symbols as keys. This is what makes it so safe for general use.

By focusing only on an object’s own enumerable string keys, Object.keys() gives you a clean, predictable snapshot of its main data. This is your safeguard against accidentally including inherited methods or special Symbol-keyed properties in your loops.

Just imagine the chaos if Object.keys() also returned inherited properties. Your array would suddenly be cluttered with keys like toString and hasOwnProperty, which you almost never want to process. By design, Object.keys() keeps your focus squarely on the data that actually belongs to the object you’re inspecting.

Bottom line: when you just want to get the main data-holding keys of an object, Object.keys() is the most reliable, straightforward, and safest choice you can make.

Finding Hidden Properties with Object.getOwnPropertyNames

Object.keys() is fantastic for most of your day-to-day work, but you’ll eventually hit a wall where it doesn’t tell you the whole story. What happens when you need to see properties that were intentionally hidden from normal loops? This is common when you’re digging into third-party libraries or complex objects where developers use non-enumerable properties to tuck away internal data or configuration settings.

This is exactly the job for Object.getOwnPropertyNames(). Think of it as the x-ray version of Object.keys(). It gives you a list of all of an object’s own string-keyed properties, regardless of whether they are set to be enumerable or not.

A workspace scene featuring a laptop displaying code, a coffee mug, and a "OBJECT. KEYS GUIDE" banner.

Seeing What Object.keys() Misses

Let’s imagine you’re debugging a component that uses a class from an external library. The original author might have stored some internal state on a non-enumerable property—something you’re not supposed to touch. But when things go wrong, you absolutely need to see it. Object.keys() will come up empty-handed here, but Object.getOwnPropertyNames() will show you exactly what’s going on.

Here’s that user profile example again, but this time with a “hidden” property:

const userProfile = { id: 123, username: ‘dev_user’ };

// We’ll add a property but make it non-enumerable Object.defineProperty(userProfile, ‘internalSessionId’, { value: ‘abc-123-def-456’, enumerable: false, // This is the magic flag! writable: true, configurable: true });

const standardKeys = Object.keys(userProfile); const allPropertyNames = Object.getOwnPropertyNames(userProfile);

console.log(‘Standard Keys:’, standardKeys); // Output: Standard Keys: [‘id’, ‘username’]

console.log(‘All Property Names:’, allPropertyNames); // Output: All Property Names: [‘id’, ‘username’, ‘internalSessionId’]

Just like that, Object.getOwnPropertyNames() found the internalSessionId. It gives you a complete inventory of the object’s direct properties, pulling back the curtain on its internal structure.

When to Actually Use It

I don’t reach for this method every day, but it’s a lifesaver in a few key situations. It’s my go-to tool for:

  • Serious Debugging: When an object is acting weird and you suspect hidden state is the culprit, this is the first thing I use to get a full picture.
  • Library Introspection: If I need to understand how a complex object from a framework or library is put together under the hood.
  • Metaprogramming: When building developer tools, linters, or anything that needs to analyze and manipulate code, you have to know about every property.

Think of it this way: Object.getOwnPropertyNames() helps you see the “source code” of an object’s properties, not just the public-facing parts. It’s an essential tool for deep analysis.

Just remember, while it’s more thorough, it still has its limits. It only returns own properties (nothing from the prototype chain) and it only finds string-based keys. If you need to find Symbols, well, there’s another tool for that job.


Key Takeaways: Object.keys vs. getOwnPropertyNames

So, how do you choose? It really comes down to one question: Am I trying to work with an object’s data, or am I trying to debug its structure?

  • Use Object.keys() for 90% of your work. It’s predictable, safe, and only gives you the enumerable properties you typically want to iterate over. It respects the author’s intent.

  • Use Object.getOwnPropertyNames() for debugging and deep inspection. When you hit a weird bug or need to reverse-engineer an object, this method shows you everything—including the properties someone tried to hide.

Keep in mind that neither of these methods will climb the prototype chain. They are both strictly focused on the object’s own properties. This is usually what you want, as it prevents accidentally pulling in properties from Object.prototype and causing chaos in your loops. And if Symbol properties come into play, neither of these will find them, which we’ll get into next.

Digging into Inherited Properties with the for…in Loop

All the methods we’ve looked at so far are designed to grab an object’s own properties. But what happens when you need to look up the prototype chain and see what an object inherits?

This is where the classic for...in loop comes in. It operates differently from everything else, giving you a way to get keys of an object in JavaScript that also exist on its prototypes.

A magnifying glass rests next to a box of index cards with 'NON-ENUMERABLE KEYS' text.

Unlike the other methods that return a neat array of keys, for...in iterates over an object’s enumerable, string-keyed properties. The crucial difference is that it keeps going up the prototype chain, including inherited properties in its search. While this can be handy for certain debugging tasks, it’s a minefield if you’re not paying close attention.

The Perils of the Prototype Chain

The biggest “gotcha” with for...in is its willingness to travel. It will happily report properties from Object.prototype, like toString, valueOf, and constructor. This can cause some really confusing bugs when you’re just trying to loop through your object’s data.

It gets worse. If another library or script on the page modifies Object.prototype—a practice called “monkey-patching”—for...in will pull those new properties into your loop, too. Suddenly, you have unexpected keys showing up and your code breaking in bizarre ways.

Your Safety Net: The hasOwnProperty Check

Because of this risk, a for...in loop should almost always be paired with an hasOwnProperty() check. This simple method is your safeguard. It returns true only if a property belongs directly to the object, not to its prototype.

Let’s look at how much of a difference this one line of code makes.

// A simple “class” constructor function Dog(name) { this.name = name; }

// Add a method to the prototype Dog.prototype.bark = function() { console.log(‘Woof!’); };

const myDog = new Dog(‘Fido’);

// Example 1: The UNSAFE for…in loop console.log(‘Looping without hasOwnProperty:’); for (const key in myDog) { console.log(key); // This will log ‘name’ AND ‘bark’ }

// Example 2: The SAFE for…in loop console.log(‘\nLooping with hasOwnProperty:’); for (const key in myDog) { if (myDog.hasOwnProperty(key)) { console.log(key); // This will ONLY log ‘name’ } }

See the difference? The hasOwnProperty() check correctly filters out the inherited bark method. The result is a clean list of the object’s own properties, just like you’d get with Object.keys().

I’ve seen for...in without a hasOwnProperty() check cause more head-scratching bugs for new JavaScript developers than almost anything else. Unless you have a very specific reason to inspect the prototype chain, always include the check.

So, when would you actually want to walk the chain? It can be useful for specialized tools, like a deep cloning function that needs to understand an object’s complete makeup, including inherited defaults. But for everyday data handling, modern methods like Object.keys() are much safer and more predictable.


My Rules of Thumb for Getting Object Keys

After years of working with JavaScript objects, I've developed a few go-to principles for choosing the right tool for the job.

📊 My Core Philosophy

  1. Prioritize Predictability — Aim for code that isn't full of surprises. Filtering out inherited properties is a huge part of that.
  2. Know Your Goal — Are you processing data or debugging the object's structure? The answer changes which tool you should reach for.
  3. Write Robust Code — The goal is to capture only the keys you need and avoid bugs caused by unexpected properties.

🕹️ My Personal Cheat Sheet

  • 🌱 Data vs. Debugging: I use `Object.keys()` for working with an object’s data. When I need to debug and see *everything*, including non-enumerable properties, I turn to `Object.getOwnPropertyNames()`.
  • 🦎 Taming `for...in`: If I absolutely have to use a `for...in` loop, the `hasOwnProperty()` check is non-negotiable. It prevents a world of hurt.
  • ⚡ Choose with Intent: I always pause and think: do I need to worry about Symbols? Inherited keys? Non-enumerable properties? That tells me which method to pick.

📈 Questions I Ask Myself

  • 💡 Am I accidentally looping over inherited properties right now?
  • 🧩 For this debugging session, do I need to see what's non-enumerable?
  • 📘 Is there a simpler, more modern way to do this than using `for...in`?

Getting All Keys with Reflect.ownKeys

So, Object.keys is great for everyday data, and Object.getOwnPropertyNames gets us closer by including non-enumerable properties. But what if you need to see everything? I’m talking about every single key that belongs directly to an object—including Symbols.

That’s where Reflect.ownKeys(), introduced in ES6, comes in. Think of it as the ultimate inventory tool. It gives you an array with all of an object’s own keys: strings, Symbols, enumerable, and non-enumerable. Nothing gets left behind.

The Most Complete Key Collector

In modern JavaScript, it’s not uncommon for libraries or even your own code to use Symbols for special properties, like unique identifiers or internal metadata, to avoid naming collisions. Reflect.ownKeys() is the only method that sees both these Symbols and the regular string keys in one go.

Let’s look at an object that has a bit of everything:

const uniqueId = Symbol(‘id’); const metadata = Symbol(‘metadata’);

const myObject = { name: ‘data-packet’, version: ‘1.0’ };

// Here’s a non-enumerable string key Object.defineProperty(myObject, ‘internalState’, { value: ‘active’, enumerable: false });

// And now we add our Symbol properties myObject[uniqueId] = ‘xyz-123’; myObject[metadata] = { lastUpdated: ‘2026-10-26’ };

const allKeys = Reflect.ownKeys(myObject);

console.log(allKeys); // Output: [‘name’, ‘version’, ‘internalState’, uniqueId, metadata]

Notice the output. We got everything: the standard keys (name, version), the “hidden” string key (internalState), and both Symbols. No other single method can give you this complete picture.

When Would You Actually Use This?

I find myself reaching for Reflect.ownKeys() when I’m writing library code or doing deep debugging. For example, if you’re building a function that clones or serializes an object, you need to be sure you’re copying it perfectly.

Reflect.ownKeys() is your go-to when you need a 100% faithful snapshot of an object. For things like metaprogramming, serialization, or advanced debugging, you can’t afford to miss any keys.

If you used Object.keys() for a cloning function, you’d silently drop any non-enumerable properties and Symbols, leading to subtle and frustrating bugs. Object.getOwnPropertyNames() is a step up, but it still leaves the Symbols behind. Reflect.ownKeys() was literally designed for these scenarios, giving you a reliable way to get a reliable way to get a full inventory without any exceptions.


JS Key Retrieval Scorecard

Ready to pick the right tool for the job every time? Here’s a quick-reference guide to help you decide.

📊 A Quick Comparison

Method Own Properties Prototype Chain Enumerable Only Symbols Best For
Object.keys() ✅ ❌ ✅ ❌ Iterating over an object’s data.
for...in ✅ ✅ ✅ ❌ Iterating an object and its prototype (use with care!).
getOwnPropertyNames() ✅ ❌ ❌ ❌ Debugging or when you need non-enumerable string keys.
Reflect.ownKeys() ✅ ❌ ❌ ✅ Getting a complete inventory of all keys, including Symbols.

🕹️ My Personal Rules of Thumb

  • 🌱 Start with Object.keys(). For probably 90% of what you do—iterating over data for display or processing—this is the safest and most direct choice. It correctly treats non-enumerable properties as “private” and ignores the prototype chain.
  • 🦎 Use Reflect.ownKeys() for total coverage. When I’m writing code that needs to clone, serialize, or deeply inspect an object, this is the only one I trust to get everything. It’s perfect for frameworks and libraries.
  • ⚡ Always guard for...in loops. If you find yourself using a for...in loop, pause and ask why. If you absolutely need properties from the prototype chain, fine—but always add a hasOwnProperty() check to avoid nasty surprises.

📈 Questions to Ask Yourself

  • Am I just trying to loop through data? (Use Object.keys()).
  • Does my code need to be aware of Symbols? (Use Reflect.ownKeys()).
  • Is this property meant to be hidden (non-enumerable), and should my code respect that?

Performance, TypeScript, And Practical Tips

When you’re grabbing keys from a JavaScript object, it’s not just about which properties you get. You also have to think about performance, type safety, and how to write code that won’t break down the line.

So, which method is fastest? Honestly, in most situations, it doesn’t matter. Object.keys() is incredibly fast and optimized in modern JavaScript engines. The performance difference between it, Object.getOwnPropertyNames(), and Reflect.ownKeys() is usually so small you’d never notice.

The for...in loop is often a bit slower because it has to do the extra work of checking the prototype chain. But again, you’d only feel that sting if you were iterating millions of times in a performance-critical loop.

My advice? Always focus on correctness first. Pick the method that gives you the exact keys you need for the job. Don’t sacrifice readable, reliable code for a tiny performance gain that your users will never see.

Two keys on a keyring, a black circular token with a white logo, and a plant on a white desk.

Working With TypeScript And keyof

If you’re using TypeScript, you have a secret weapon: the keyof operator. It’s a game-changer. It analyzes an object’s type and creates a new union type composed of its literal key names.

This is huge for writing type-safe functions, as it guarantees you’re only trying to access properties that actually exist.

interface User { id: number; name: string; }

// The type of UserKeys becomes “id” | “name” type UserKeys = keyof User;

function getProperty(obj: User, key: UserKeys) { return obj[key]; // TypeScript knows ‘key’ is a valid key of User }

// This works beautifully const userName = getProperty({ id: 1, name: “Alex” }, “name”);

// This will throw a compile-time error, saving you from a runtime bug! // const userRole = getProperty({ id: 1, name: “Alex” }, “role”); // Error: Argument of type ‘“role”’ is not assignable to parameter of type ‘“id” | “name”’.

Using keyof lets the TypeScript compiler catch typos and logic errors for you, long before your code ever hits the browser.

Practical Tips For Writing Robust Code

Building resilient code means thinking about the edge cases. To get a better handle on this, it’s worth brushing up on how JavaScript’s type system works. Here are a couple of essential tips I’ve learned over the years:

  • Guard Against null and undefined: Every single one of these methods will throw a TypeError if you pass them null or undefined. Always add a check to make sure your object exists before trying to get its keys. It’s a simple step that prevents a whole class of runtime errors.

  • Master Your DevTools: Knowing how to get keys is one thing, but being able to quickly inspect objects is just as important. Get comfortable with your browser’s developer tools. Learning to navigate something like the Safari browser console effectively is an invaluable skill for any developer.

Method Comparison Breakdown

With four different ways to get keys, it can be tough to remember which one does what. I’ve put together this quick comparison table to help you choose the right tool for your specific task at a glance.

Method Handles Enumerability Handles Symbols Handles Prototype Chain Best For
Object.keys() ✅ Only own, enumerable ❌ No ❌ No Getting the “standard” string keys you’d typically iterate over.
for...in ✅ Own & inherited, enumerable ❌ No ✅ Yes When you need to inspect an object and its prototype chain.
Object.getOwnPropertyNames() ❌ Own, enumerable & non-enumerable ❌ No ❌ No Getting all string keys on an object itself, ignoring symbols.
Reflect.ownKeys() ❌ Own, enumerable & non-enumerable ✅ Yes ❌ No The most complete picture; getting every single own key, including symbols.

Each method has a distinct purpose. Object.keys() is your daily driver, for...in is for deep dives, getOwnPropertyNames is for finding hidden string keys, and Reflect.ownKeys is for when you absolutely need everything.

Common Questions & Gotchas

When you’re wrangling object keys in JavaScript, a few questions and tricky situations always seem to come up. Let’s clear the air on some of the most common ones I see developers run into.

What Is the Fastest Way to Get Object Keys?

Everyone wants the fastest method, but honestly, the performance differences are usually tiny in the real world. For most day-to-day coding, Object.keys() is plenty fast and reliable.

It’s far more important to pick the method that actually fits what you’re trying to do—like whether you need to see Symbol keys or non-enumerable properties. Don’t get caught up in premature optimization. Modern JavaScript engines have made Object.keys() so efficient that it’s the perfect default choice.

How Can I Get Keys in a Guaranteed Order?

Good news! Ever since ES2015, you can actually count on the order of keys returned by most methods. Object.keys(), Object.getOwnPropertyNames(), and Reflect.ownKeys() all follow a specific, predictable order:

  1. First, any keys that look like integers will be returned in ascending numerical order.
  2. Next come all other string keys, in the exact order they were originally added to the object.
  3. Finally, any Symbol keys are returned, also in the order they were added.

The one major outlier here is the old for...in loop. Its iteration order is not guaranteed and can change between different JavaScript engines. You should never rely on for...in for ordered iteration.

Why Does Object.keys() Return an Empty Array for a New Date()?

Ah, this is a classic trip-up! It’s a fantastic real-world lesson in how Object.keys() actually works under the hood. It only returns an object’s own enumerable properties.

Built-in objects like Date have their methods and properties (like getFullYear) on their prototype (Date.prototype), not directly on the instance you create. So, when you call Object.keys(new Date()), it correctly returns an empty array because that specific Date instance doesn’t have any own enumerable properties. Getting a grip on this is fundamental to debugging your code with tools like Visual Studio Code.


Final Takeaways: Mastering Object Keys

Now you have the tools to confidently grab object keys for any situation, from simple data loops to deep debugging dives.

📊 Key Metrics to Remember

  • Code Correctness: Does the method you picked return exactly the keys you need (including Symbols or non-enumerables)?
  • Bug Avoidance: Are you preventing errors by not accidentally looping over prototype properties?
  • Selection Speed: Can you quickly identify the right tool for the job without hesitation?

🕹️ My Go-To Rules of Thumb

  • 🌱 Data vs. Debug: Use Object.keys() for everyday data tasks. Pull out Reflect.ownKeys() for deep, comprehensive inspection.
  • 🦎 Guard Your Loops: If you absolutely must use a for...in loop, always pair it with a hasOwnProperty() check to stay safe.
  • ⚡ Order Matters: Stick to modern methods like Object.keys() for predictable, ordered results. Never trust for...in when order counts.

📈 Questions to Ask Yourself

  • 💡 Am I 100% sure this object doesn’t have Symbol keys that I’m accidentally ignoring?
  • 🧩 Is this for...in loop really necessary, or could Object.keys() get the job done more safely?
  • 📘 Did I add a check to make sure the object isn’t null or undefined before I try to get its keys?

Master Your JS Object Keys

Confidently grab object keys for any situation, from simple data loops to deep debugging dives.

📊 Key Retrieval Metrics

  1. Code Correctness — Are you getting the exact keys you need (e.g., enumerable, Symbol)?
  2. Bug Avoidance — Have you eliminated bugs from prototype chain properties?
  3. Method Selection Speed — How quickly can you choose the right tool for the task?

🕹️ My Cheatcodes

  • 🌱 Data vs. Debug — Use `Object.keys()` for data; use `Reflect.ownKeys()` for full inspection.
  • 🦎 Guard `for...in` — Always use `hasOwnProperty()` to prevent looping over the prototype chain.
  • ⚡ Trust Modern Order — Rely on `Object.keys()` for guaranteed key order; never `for...in`.

✅ Before Looping

  • 🔍 Check if `Object.keys()` is sufficient for your needs.
  • 📊 Determine if you need to account for Symbol or non-enumerable keys.
  • 🧭 Add a guard for `null` or `undefined` objects.
  • 📈 If using `for...in`, include a `hasOwnProperty()` check.

Gallery

Carousel image
Carousel image
Carousel image
Carousel image
Carousel image
Carousel image
Carousel image
Carousel image

FAQ

Frequently asked questions

Haven't found what you're looking for?

Connecting your GitHub org gives you team-level AI adoption signals out of the box — PR throughput, review patterns, and Acceleration Reports — without anyone installing anything else.

No. GitHub alone unlocks the team dashboard. For richer per-engineer insights (AI Stack, AI Skills, agent activity), engineers can install Zest in Cursor or Claude Code at any time.

Zest tracks blockers across tasks and prompts. By spotting weak spots and highlighting working strategies, your team keeps momentum and ships code faster.

Copilot metrics tell you usage; Zest tells you outcomes. We measure AI compute impact on PR throughput, code review quality, and agent autonomy levels — across Cursor, Claude Code, Codex, and any model your team runs.

Adoption charts show who's using AI, how often, and where value is gained. Teams and enterprises can drive toward 100% usage — ensuring everyone benefits.

Yes. Connect your GitHub org and you'll get the L2 dashboard — leaderboards, AI metrics, and team insights — with no credit card needed.