In JavaScript, Enumerability is a hidden attribute of object properties that determines whether a property is “visible” during certain iteration operations. It acts as a gatekeeper, filtering which data points should be exposed when an object is traversed. By default, most properties created via simple assignment or object literals are enumerable, but internal engine properties and those defined via specific metadata methods often are not.

1. The Enumerable Internal Attribute

Every property in JavaScript is governed by a Property Descriptor. Within this descriptor is a Boolean flag called enumerable.

  • enumerable: true: The property will be included in loops like for...in and methods like Object.keys().

  • enumerable: false: The property is “hidden” from these operations, though it is still fully accessible if you know its exact name.

2. Default Behavior and Property Creation

The way you create a property determines its default enumerability.

A. Assignment and Literals

When you define a property using an object literal or dot notation, it is enumerable by default. This is intended for standard data storage that you would naturally want to iterate over.

B. Object.defineProperty()

When using this method to define a property, the enumerable attribute defaults to false unless you explicitly state otherwise. This allows developers to attach “metadata” or internal state to an object that shouldn’t interfere with data processing loops.

JavaScript

const user = { name: "Julian" }; // Enumerable by default

Object.defineProperty(user, 'internalID', {
  value: 9982,
  enumerable: false // Hidden from loops
});

console.log(Object.keys(user)); // ["name"]
console.log(user.internalID);   // 9982 (Still accessible!)

4. Why Use Non-Enumerable Properties?

A. Protecting Internal Logic

If you are building a library, you might want to attach a version number or a configuration object to an instance. If that property is enumerable, a user calling Object.assign({}, yourObject) or JSON.stringify(yourObject) will accidentally copy or serialize your internal metadata. By making it non-enumerable, you keep the object “clean.”

B. Prototype Methods

This is the primary reason for the existence of the flag. If you add a method to Object.prototype, you wouldn’t want it to show up every time someone runs a for...in loop on a simple object. Native methods like .toString() are non-enumerable for this exact reason.

JavaScript

// Example: Adding a helper to all objects
Object.defineProperty(Object.prototype, 'logMe', {
  value: function() { console.log(this); },
  enumerable: false // Prevents breaking for...in loops globally
});

5. Enumerability and the Spread Operator

The Spread Operator (...) and Object.assign() only copy enumerable, own properties. This is a common “gotcha”: if you have a non-enumerable property that you intend to clone into a new object, these standard methods will fail to pick it up.

JavaScript

const source = {};
Object.defineProperty(source, 'hidden', { value: 'secret', enumerable: false });

const clone = { ...source };
console.log(clone.hidden); // undefined

Categorized in:

Javascript, Js Basic,