Volume 2

Q107: Property Descriptors and Object Shapes

Difficulty: SeniorFrequency: MediumAnswer time: 8-12 minutes

What Interviewers Want To Evaluate

This question checks whether you understand that JavaScript properties have metadata, not only values. Interviewers want data descriptors, accessor descriptors, enumerability, writability, configurability, object freezing, hidden classes or shapes, and practical trade-offs.

Senior answers should connect property descriptors to library design, debugging, immutability, performance, and framework behavior.

Short Interview Answer

A JavaScript property descriptor describes how a property behaves. A data descriptor has a value and writable flag. An accessor descriptor has get and set functions. Both can be enumerable and configurable. Engines also track object shapes internally so objects with stable property layouts can be optimized. Descriptors control behavior at the language level, while object shapes help engines optimize common access patterns.

Detailed Interview Answer

Most code creates properties with assignment:

const user = {};
user.name = "Asha";

This creates a normal writable, enumerable, configurable data property.

Property descriptors let you inspect or define the exact behavior.

const descriptor = Object.getOwnPropertyDescriptor(user, "name");

console.log(descriptor);

Data Descriptors

A data descriptor stores a value.

Object.defineProperty(user, "role", {
  value: "admin",
  writable: true,
  enumerable: true,
  configurable: true,
});

The main fields are:

value: actual stored value
writable: whether assignment can change it
enumerable: whether it appears in enumeration
configurable: whether descriptor can be changed or deleted

Accessor Descriptors

Accessor descriptors use functions instead of a stored value.

const account = {
  firstName: "Asha",
  lastName: "Rao",
};

Object.defineProperty(account, "fullName", {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
  enumerable: true,
});

console.log(account.fullName);

Accessors are useful when a property should be computed, validated, or proxied.

Data vs Accessor Rules

A descriptor cannot be both a data descriptor and an accessor descriptor.

This is invalid:

Object.defineProperty({}, "x", {
  value: 1,
  get() {
    return 1;
  },
});

You choose stored value behavior or getter/setter behavior.

Enumerability

Enumerable properties appear in common enumeration patterns.

const user = {};

Object.defineProperty(user, "id", {
  value: "u1",
  enumerable: false,
});

user.name = "Asha";

console.log(Object.keys(user)); // ["name"]

Non-enumerable properties can still be accessed directly.

Configurability

configurable: false means the property cannot be deleted and its descriptor cannot be freely changed.

Object.defineProperty(user, "id", {
  value: "u1",
  configurable: false,
});

delete user.id; // fails or throws in strict mode

This can protect internal behavior but can also make testing and patching harder.

Writability

writable: false prevents assignment from changing the stored value.

Object.defineProperty(user, "status", {
  value: "active",
  writable: false,
});

user.status = "disabled";

In strict mode this throws. Outside strict mode it may fail silently.

Object.freeze, seal, and preventExtensions

JavaScript has higher-level object restriction APIs.

Object.preventExtensions: no new properties
Object.seal: no new properties and no deletion
Object.freeze: no new properties, no deletion, no writing data properties

These are shallow operations. Nested objects remain mutable unless also frozen.

Object Shapes

JavaScript engines optimize property access by tracking object layout. This internal layout is often called a hidden class, shape, or structure depending on the engine.

function createUser(name, role) {
  return {
    name,
    role,
    active: true,
  };
}

Objects created with the same property order and structure are easier for engines to optimize.

Why Shapes Matter

If objects constantly gain different properties in different orders, the engine may have a harder time optimizing property access.

const a = {};
a.name = "Asha";
a.role = "admin";

const b = {};
b.role = "admin";
b.name = "Asha";

These two objects may have different internal shape transitions even though they end with similar visible properties.

Senior Trade-Offs

Do not over-optimize normal application code around hidden classes. Write clear stable object construction first.

It matters most in hot paths, data normalization, rendering-heavy code, and libraries that create many objects.

Code or Design Example

function normalizeUser(raw) {
  return {
    id: String(raw.id),
    name: String(raw.name),
    role: raw.role ?? "viewer",
    active: Boolean(raw.active),
  };
}

This gives every normalized user a predictable shape.

Debugging Workflow

When object behavior is confusing:

inspect Object.getOwnPropertyDescriptor
check Object.keys vs Reflect.ownKeys
check whether a getter is running
check freeze, seal, or preventExtensions
check strict mode assignment behavior
check dynamic property creation in hot paths

Interview Framing

Explain descriptors first because they are language-level behavior. Then mention object shapes as engine optimization details that reward stable object construction.

Revision Notes

Descriptors define what a property is allowed to do. Shapes are engine internals that make repeated property access faster when objects are predictable.

Key Takeaways

Property behavior is more than key-value storage. Understanding descriptors helps debug libraries, accessors, immutability, and unexpected assignment behavior.

Follow-Up Questions

  1. What is a property descriptor?
  2. What is the difference between data and accessor descriptors?
  3. What does enumerable control?
  4. What does configurable control?
  5. What does writable control?
  6. Is Object.freeze deep?
  7. Why do engines care about object shapes?
  8. Should application code always optimize around hidden classes?
  9. How can getters make debugging harder?
  10. When would you use Reflect.ownKeys?

How I Would Answer This In A Real Interview

I would describe descriptors as metadata for object properties, compare data and accessor descriptors, then explain that engines optimize stable object shapes. I would add that this matters in libraries and hot paths, while normal product code should prefer clarity and predictable construction.