if you’ve ever looped over an object in JavaScript and wondered why some properties don’t show up enumerable is your answer.
Let’s break it down.
What Is Enumerable?
Every property in a JavaScript object has hidden metadata called property descriptors. One of those descriptors is enumerable a simple true or false flag that controls whether a property appears when you iterate over an object.
// Every property secretly looks like this:
{
value: 42,
writable: true,
enumerable: true, // 👈 this one
configurable: true
}
What Does enumerable: true vs false Actually Do?
When enumerable is true, the property shows up in:
for...inloopsObject.keys()Object.values()Object.entries()- Spread operator
{ ...obj }
When enumerable is false, the property is hidden from all of those, but it’s not gone. You can still access it directly.
A Simple Example
const obj = {};
Object.defineProperty(obj, 'hidden', {
value: 'secret',
enumerable: false
});
Object.defineProperty(obj, 'visible', {
value: 'hello',
enumerable: true
});
console.log(Object.keys(obj)); // ['visible']
console.log(obj.hidden); // 'secret' ← still accessible!
console.log(obj.visible); // 'hello'
for (let key in obj) {
console.log(key); // 'visible' only
}
Notice: hidden is not deleted, it just doesn’t show up in loops or key listings.
Normal Assignment = Enumerable by Default
const user = { name: 'Alice', age: 30 };
// Both properties are enumerable: true by default
console.log(Object.keys(user)); // ['name', 'age']
When you assign properties the normal way (using = or object literals), they’re enumerable by default.
How to Check Enumerability
const obj = { name: 'Alice' };
// Method 1: propertyIsEnumerable
console.log(obj.propertyIsEnumerable('name')); // true
// Method 2: getOwnPropertyDescriptor
const descriptor = Object.getOwnPropertyDescriptor(obj, 'name');
console.log(descriptor.enumerable); // true
Real-World Example: Prototype Methods
This is why Array.prototype.map doesn’t appear when you loop over an array:
const arr = [1, 2, 3];
for (let key in arr) {
console.log(key); // '0', '1', '2' — NOT 'map', 'filter', etc.
}
// Those methods exist but are non-enumerable
const desc = Object.getOwnPropertyDescriptor(Array.prototype, 'map');
console.log(desc.enumerable); // false
If they were enumerable, every for...in loop over an array would expose all built-in methods — a mess you definitely don’t want.
Creating Non-Enumerable Properties
Use Object.defineProperty() to hide internal properties:
const person = { name: 'Bob' };
Object.defineProperty(person, '_id', {
value: 'abc123',
enumerable: false, // hidden from loops
writable: false, // can't change
configurable: false // can't delete or redefine
});
console.log(Object.keys(person)); // ['name']
console.log(person._id); // 'abc123'
This is a clean way to attach metadata to an object without polluting your data output.
Spread and Non-Enumerable Properties
const original = {};
Object.defineProperty(original, 'secret', {
value: 'hidden',
enumerable: false
});
original.name = 'Alice';
const copy = { ...original };
console.log(copy.name); // 'Alice'
console.log(copy.secret); // undefined ← not copied!
Summary
enumerable: true, property appears in loops and key methods (default for normal assignments)enumerable: false,property is hidden from loops but still accessible directly- Use
Object.defineProperty()to control enumerability - Built-in prototype methods (
map,filter, etc.) are non-enumerable by design - Spread
{ ...obj }only copies enumerable own properties


