Lesson 56: Mastering JavaScript Property flags and descriptors with challenges!

manoj ymkmanoj ymk
6 min read

🧩 What Are Property Descriptors?

In JavaScript, every property in an object has attributes (a.k.a. property flags):

FlagMeaning
writableCan the value be changed with assignment (=)?
enumerableWill it appear in for...in, Object.keys(), etc.?
configurableCan the property be deleted or reconfigured (flags changed)?

By default, when a property is created using object literal {}, all flags are true.


βœ… Access Descriptors

const user = { name: "Alice" };

console.log(Object.getOwnPropertyDescriptor(user, 'name'));
/*
{
  value: 'Alice',
  writable: true,
  enumerable: true,
  configurable: true
}
*/

βœ… Define/Modify Descriptors

Object.defineProperty(user, 'name', {
  writable: false,
  configurable: false
});

This makes name a constant β€” can't change or delete.


βœ… Real-World Use

let settings = {};
Object.defineProperty(settings, 'version', {
  value: '1.0.0',
  writable: false,
  enumerable: false,
  configurable: false
});

πŸ“Š Descriptor Flowchart

                        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚ Define propβ”‚
                        β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
     β–Ό                        β–Ό                          β–Ό
writable: false      enumerable: false        configurable: false
Can't assign         Skipped in               Can't delete
new value            loops/keys()             or redefine flags

πŸ”Ή 2. Fill Any Gaps

βœ… Defaults from defineProperty

If you omit any flags, they default to false (not true).

Object.defineProperty(obj, "a", { value: 10 });
// => a is now non-writable, non-enumerable, non-configurable

βœ… Symbolic and Non-enumerable Properties

These do not appear in:

  • for...in

  • Object.keys()
    But they do appear in:

  • Object.getOwnPropertyDescriptors

  • Reflect.ownKeys()


βœ… You Can’t Revert configurable: false

Once you mark a property as configurable: false, you cannot:

  • delete it

  • change enumerable

  • change configurable again

  • change writable from false to true (but true β†’ false is allowed)


βœ… Shallow Cloning with Descriptors

let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(source));

βœ… Includes non-enumerable and symbol keys.
❌ Doesn’t deep-clone nested objects.


βœ… Strict Mode Behavior

'use strict';
Object.defineProperty(obj, 'x', { writable: false });
obj.x = 10; // ❌ TypeError

Without 'use strict', this fails silently!


βœ… Define Multiple Properties at Once

Object.defineProperties(user, {
  name: { value: 'Alice', writable: false },
  age: { value: 30, writable: true }
});

βœ… Sealing / Freezing Objects

MethodPrevent AddPrevent DeletePrevent WritePrevent Reconfig
Object.preventExtensions()βœ…βŒβŒβŒ
Object.seal()βœ…βœ…βŒβœ…
Object.freeze()βœ…βœ…βœ…βœ…

πŸ”Ή 3. Challenge Me Deeply

🟒 Basic

  1. Define a property secret on an object that is not writable, not enumerable, and not configurable. Try modifying and deleting it.

  2. Use Object.getOwnPropertyDescriptor() to inspect a built-in object like Math.PI.

  3. Create a property score that is writable and enumerable but not configurable.

🟑 Intermediate

  1. Write a function makeConstant(obj, key, value) that defines a constant property.

  2. Clone an object preserving all flags, including symbol keys.

  3. Create an object with one enumerable property and one non-enumerable property. Use for...in and Object.keys() to show the difference.

πŸ”΄ Advanced

  1. Write a deepFreeze(obj) function that recursively freezes all nested properties.

  2. Create a descriptor-based defineClassProperty(obj, key, value) function that simulates a private class member (non-enumerable, non-writable).

  3. Try redefining a non-configurable property. Catch and explain the error.

  4. Create a function secureObject(obj) that seals and logs all current property descriptors.

🎯 Aha! Brain-Twister

  1. You define a property with writable: true, configurable: false, then later change writable to false. Can you change it back to true? Why or why not?

πŸ”Ή 4. Interview-Ready Questions

βœ… Concept Questions

  • What is the difference between writable, enumerable, and configurable?

  • How does Object.defineProperty() differ from assigning properties directly?

  • When do Object.keys() and for...in skip properties?

βœ… Scenario-Based

  • How would you protect certain properties of a settings object from modification or deletion?

  • You’re cloning an object but notice some properties are missing. Why might this happen?

βœ… Debugging-Style

  • You defined a property using defineProperty, but assignment to it silently fails. What's likely the reason?

  • You froze an object, yet a nested property was still modified. Why?

βœ… Best Practices

βœ… Do:

  • Use Object.freeze() for immutable config objects.

  • Use Object.defineProperties() for grouped definitions.

❌ Don’t:

  • Mutate built-in objects like Math, Object.prototype.

  • Assume for...in shows all properties.


πŸ”Ή 5. Real-World Usage

  • πŸ”§ Library Authors: Use descriptors to define framework internals (e.g., Vue’s reactivity system).

  • πŸ” Security: Define read-only tokens or config properties.

  • πŸš€ APIs: Hide internal implementation from public API (non-enumerable).

Example: Vue 3 Reactivity System

Object.defineProperty(obj, '__isReactive', {
  value: true,
  enumerable: false,
  configurable: false
});

πŸ”Ή 6. Remember Like a Pro

🧠 Mnemonic: WEC β€” β€œWe Eat Cookies”

  • Writable – Can you Write to it?

  • Enumerable – Will you Enumerate it?

  • Configurable – Can you Change or delete it?


🧾 Cheatsheet

Object.defineProperty(obj, 'x', {
  value: 42,
  writable: false,     // can't change value
  enumerable: false,   // hidden from loops
  configurable: false  // can't delete or redefine
});
ScenarioWritableEnumerableConfigurable
Default literalβœ…βœ…βœ…
defineProperty default❌❌❌

πŸ”Ή 7. Apply It in a Fun Way

🎯 Mini Project: SecureConfig Builder

Build a utility to generate an app config object where all properties are:

  • Non-writable

  • Non-configurable

  • Optionally enumerable

βœ… Steps:

  1. Create a createSecureConfig(configObj, isEnumerable = false) function.

  2. Use Object.defineProperties() to lock down each key in configObj.

  3. Add Object.freeze() to seal the object globally.

const config = createSecureConfig({ apiKey: "1234", debug: false });
config.apiKey = "9999"; // silently ignored or error in strict mode

βž• Extension

  • Add support for deep config freeze.

  • Include a getter/setter interface for internal changes (controlled).


🧠 Optional – Extra Value

πŸ” Open Source Projects Using This

  • Vue.js (for reactivity markers)

  • Lodash (for internal method protection)

  • Express.js (on request/response objects)


⚠️ Common Mistakes

  • Forgetting that defineProperty defaults all flags to false!

  • Expecting Object.keys() to show all properties, even non-enumerable ones.

  • Assuming delete obj.prop always works β€” not for configurable: false.


🏎 Performance Tips

  • Avoid excessive use of Object.defineProperty in tight loops.

  • Freezing large nested objects can be costly β€” use lazily.


βœ… Polyfills / Alternatives

In older environments:

if (!Object.defineProperty) {
  // Polyfill basic read-only functionality
}

Use libraries like Lodash _.defaultsDeep() or deep-freeze utilities for safer immutable patterns.

0
Subscribe to my newsletter

Read articles from manoj ymk directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

manoj ymk
manoj ymk