LESSON 17 · INTERMEDIATE4 min read

Object Literals, Property Attributes & Object.defineProperty

Dive beneath object literals. Explore property descriptors: writable, enumerable, configurable, getters, setters, and object freezing.

ES2024 / Node.js 20+Free to readModule 5 of 10
YOUR NEXT SMALL WINS

By the end, you’ll be able to…

  • Inspect and configure property descriptors using Object.getOwnPropertyDescriptor and Object.defineProperty.
  • Contrast data descriptors with accessor descriptors (getters/setters).
  • Enforce immutability boundaries using Object.preventExtensions, Object.seal, and Object.freeze.

01Beyond Simple Key-Value Pairs

In everyday JavaScript, we create objects using simple object literal syntax: const user = { name: 'Ada' }. While this appears to be a basic dictionary, ECMAScript objects are vastly more sophisticated. Every property on an object is governed by a Property Descriptor: a record of internal boolean flags that control how the JavaScript engine treats that property.

There are two kinds of property descriptors: Data Descriptors and Accessor Descriptors. Data descriptors possess a value and a writable flag. Accessor descriptors define get and set functions. Both share enumerable (whether the key appears in for...in loops and Object.keys()) and configurable (whether the descriptor can be modified or deleted).

Every property on a JavaScript object is governed by an underlying set of attributes called Property Descriptors. When you create a property using standard assignment (obj.x = 10), JavaScript creates a Data Descriptor where writable, enumerable, and configurable all default to true.

However, when defining properties via Object.defineProperty, omitted descriptor flags default to false! A property created with Object.defineProperty(obj, 'id', { value: 101 }) will be read-only (writable: false), invisible to for...in loops and Object.keys (enumerable: false), and impossible to delete or reconfigure (configurable: false).

example.js⚡JavaScript
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

02Defining Read-Only and Hidden Properties

Using Object.defineProperty, you can configure properties with surgical precision. If you set writable: false, attempts to mutate the property in strict mode will throw a TypeError: Cannot assign to read only property.

If you set enumerable: false, the property becomes invisible to Object.keys(), Object.entries(), and for...in loops, while remaining directly accessible via direct property lookup. This is how native JavaScript engine APIs hide built-in methods like Array.prototype.toString from polluting your for...in loops.

example.js⚡JavaScript
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

03Object Integrity Levels: Freeze vs Seal

ECMAScript provides three built-in integrity levels to protect objects from modification: Object.preventExtensions(), Object.seal(), and Object.freeze().

Object.preventExtensions(obj) prevents new properties from being added, but existing properties can still be modified or deleted. Object.seal(obj) goes further: no new properties can be added and existing properties cannot be deleted (configurable: false), but existing writable properties can still be updated. Object.freeze(obj) is the ultimate lock: it seals the object and marks all data properties as writable: false, creating a shallowly immutable object.

example.js⚡JavaScript
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.

04Why This Matters in Production

In production architecture, libraries and frameworks rely heavily on property descriptors to protect internal state, implement reactive getters and setters (Vue 2 and MobX), and enforce domain invariants in Domain-Driven Design (DDD).

Note that Object.freeze is shallow: nested objects inside a frozen parent can still be mutated unless you apply a recursive deepFreeze utility. Enforcing immutability prevents rogue third-party packages or junior code from accidentally overriding critical shared configurations.

LET IT CLICK

The ideas worth keeping.

  • Property descriptors control writable, enumerable, and configurable behaviors.
  • Object.freeze creates shallowly immutable objects; deep immutability requires recursive freezing.
  • Non-enumerable properties provide clean encapsulation without polluting iteration loops.
TURN UNDERSTANDING INTO SOMETHING YOU CAN DO

Make it yours.

Type and test your code right here in the interactive workspace. Run your solution, check the output, and compare with the worked guide when ready.

01

Exercise 1: Deep Freeze Implementation

Write a function deepFreeze(obj) that recursively freezes an object and all its nested object properties.

your_turn_1.js⚡JavaScript
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.jsJavaScript
function deepFreeze(obj) {  Object.freeze(obj);  for (const value of Object.values(obj)) {    if (value && typeof value === 'object' && !Object.isFrozen(value)) {      deepFreeze(value);    }  }  return obj;}

Recursively traversing object values and freezing each nested heap object establishes true, deep immutability.

02

Exercise 2: Non-Enumerable Secret Key

Attach a non-enumerable property secretToken to an existing object authState so it does not appear in Object.keys() but can be read directly.

your_turn_2.js⚡JavaScript
Ctrl + ↵
Output
Click Run or press Ctrl+Enter
Output will appear here after you run the code.
Explore a worked solution
one_possible_solution.jsJavaScript
function attachSecret(authState, token) {  Object.defineProperty(authState, 'secretToken', {    value: token,    enumerable: false,    writable: false,    configurable: false  });  return authState;}

Setting enumerable: false hides the property from enumeration loops and serialization utilities like JSON.stringify.

A SMALL QUESTION. A USEFUL NEXT STEP.

What stayed with you?

Check the ideas while they’re fresh. Your answers stay in this page and feedback appears as you go.

A LITTLE ACTIVE RECALL0 / 3 answered
01What happens in strict mode when attempting to modify a property with writable: false?
02What does Object.freeze() do to an object?
03Is Object.freeze deep or shallow by default?
⚡CODING CHALLENGE
🔒Required to Mark Lesson Complete

Exercise 1: Deep Freeze Implementation

Write a function deepFreeze(obj) that recursively freezes an object and all its nested object properties.

EXPECTED OUTPUT
[Process exited with code 0 - No output]
main.jsJavaScript
Ctrl + ↵
Execution Console
Click "Run & Verify" (or press Ctrl + Enter) to test your code against the challenge.

Keep exploring.

The official references behind this lesson, when you want a second angle or the full details.

One lesson closer.

Reading is a start. Trying it yourself makes the connection.

Solve the hands-on coding challenge in the knowledge section to verify your code and unlock lesson completion.