JavaScript Enumerable Properties and Loops Explained

In JavaScript, object properties have hidden internal attributes that dictate how they behave during execution. One of the most important attributes is the enumerable flag, which determines whether a property can be discovered during iteration. This article explains what enumerable properties are, how to control their visibility using native JavaScript methods, and how they directly affect loops and serialization methods such as for...in, Object.keys(), and JSON.stringify().

What Are Enumerable Properties?

An enumerable property is an object property with an internal enumerable attribute set to true. When a property is enumerable, it can be viewed and accessed by iteration mechanisms that traverse an object’s keys.

By default, every property created via simple assignment or through an object literal is enumerable:

const user = {
  name: "Alice",
  age: 30
};

// Both 'name' and 'age' are enumerable by default

You can define non-enumerable properties using Object.defineProperty() or Object.defineProperties() by explicitly setting the enumerable descriptor to false:

const user = {
  name: "Alice"
};

Object.defineProperty(user, "id", {
  value: 101,
  enumerable: false,
  writable: true,
  configurable: true
});

In this example, user.id exists and can be accessed directly via user.id, but it will be hidden from standard iteration methods.

How Enumerable Properties Affect Loops and Methods

The enumerable flag dictates which built-in JavaScript operations will include a property.

1. The for...in Loop

The for...in loop traverses all enumerable properties of an object, including inherited enumerable properties from its prototype chain.

const car = {
  make: "Toyota"
};

Object.defineProperty(car, "secretSerial", {
  value: "XYZ123",
  enumerable: false
});

for (let key in car) {
  console.log(key); // Outputs only: "make"
}

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

These static methods return arrays of an object’s own enumerable property names, values, or key-value pairs, completely ignoring non-enumerable properties and prototype properties.

console.log(Object.keys(car));    // ["make"]
console.log(Object.values(car));  // ["Toyota"]
console.log(Object.entries(car)); // [["make", "Toyota"]]

3. JSON.stringify()

JSON.stringify() serializes only an object’s own enumerable properties into a JSON string. Any non-enumerable property is omitted from the resulting string.

console.log(JSON.stringify(car)); // '{"make":"Toyota"}'

4. Object Spread and Object.assign()

Both the spread operator (...) and Object.assign() copy only own enumerable properties from a source object to a target object.

const copy = { ...car };
console.log(copy.secretSerial); // undefined

How to Check and Access Non-Enumerable Properties

If you need to verify or retrieve non-enumerable properties, JavaScript provides specialized methods:

Summary of Loop Behavior

Method / Operator Includes Own Enumerable Includes Inherited Enumerable Includes Non-Enumerable
for...in Yes Yes No
Object.keys() Yes No No
Object.assign() / Spread (...) Yes No No
JSON.stringify() Yes No No
Object.getOwnPropertyNames() Yes No Yes
Reflect.ownKeys() Yes No Yes