Property Descriptors

Back

Loading concept...

🔐 Property Descriptors: The Secret Control Panel of Objects

The Treasure Chest Analogy

Imagine you have a magical treasure chest. Inside are your precious things—gold coins, gems, and special keys. But here’s the cool part: each item in the chest has an invisible control panel that decides:

  • Can someone see this item when they peek inside?
  • Can someone change this item?
  • Can someone remove this item forever?

In JavaScript, every property in an object has this same secret control panel. It’s called a Property Descriptor!


🎯 What Are Property Descriptors?

A property descriptor is like a set of rules attached to each property. These rules tell JavaScript:

  1. value — What’s stored here?
  2. writable — Can this be changed?
  3. enumerable — Does this show up in lists?
  4. configurable — Can these rules be changed later?

👀 Peeking at the Control Panel

const treasure = { gold: 100 };

const descriptor = Object.getOwnPropertyDescriptor(
  treasure,
  'gold'
);

console.log(descriptor);
// {
//   value: 100,
//   writable: true,
//   enumerable: true,
//   configurable: true
// }

Think of it like this: When you normally create an object, all switches are ON by default!


🔧 Object.defineProperty: The Master Key

Object.defineProperty() lets you create properties with custom rules. It’s like being the boss of your treasure chest!

Basic Structure

Object.defineProperty(object, 'propertyName', {
  value: 'something',
  writable: true,
  enumerable: true,
  configurable: true
});

🛡️ Making a Read-Only Property

const hero = {};

Object.defineProperty(hero, 'name', {
  value: 'Superman',
  writable: false,     // Can't change!
  enumerable: true,
  configurable: true
});

hero.name = 'Batman';  // This fails silently!
console.log(hero.name); // Still 'Superman'

Story Time: Imagine Superman’s name is carved in stone. No matter how hard you try to scratch it out, it stays “Superman” forever!


📋 Enumerable: Hide and Seek Champion

The enumerable property decides if something shows up when you list all properties.

const spy = {};

Object.defineProperty(spy, 'secretCode', {
  value: '007',
  enumerable: false  // Hidden!
});

spy.name = 'James';  // Normal property

console.log(Object.keys(spy));
// ['name'] - secretCode is invisible!

// But you can still access it directly
console.log(spy.secretCode); // '007'

Think of it like this: The secret code wears an invisibility cloak. It exists, but nobody sees it in the list!

Where Enumerable Matters

// for...in loops skip non-enumerable
for (let key in spy) {
  console.log(key); // Only 'name'
}

// JSON.stringify ignores non-enumerable
console.log(JSON.stringify(spy));
// {"name":"James"} - no secretCode!

🔒 Configurable: The Lock That Locks Itself

When configurable is false, you cannot:

  • Delete the property
  • Change any descriptor settings (except value if writable is true)
const vault = {};

Object.defineProperty(vault, 'password', {
  value: 'secret123',
  writable: true,
  configurable: false  // Locked forever!
});

// Try to delete it
delete vault.password; // Fails!
console.log(vault.password); // Still there

// Try to make it enumerable
Object.defineProperty(vault, 'password', {
  enumerable: true  // ERROR!
});

Warning: Once configurable is false, there’s no going back. It’s like super glue—permanent!

graph TD A["configurable: true"] -->|Can change| B["Any descriptor"] A -->|Can| C["Delete property"] D["configurable: false"] -->|Cannot change| E["Descriptors"] D -->|Cannot| F["Delete property"] D -->|Exception| G["Can still change value if writable: true"]

🎭 Getters and Setters: The Smart Guards

Instead of storing a value directly, you can use getters and setters—smart functions that run when you read or write!

🎬 The Movie Ticket Counter

const theater = {
  _tickets: 100,  // Private storage

  get availableTickets() {
    return this._tickets;
  },

  set availableTickets(value) {
    if (value < 0) {
      console.log("Can't have negative tickets!");
      return;
    }
    this._tickets = value;
  }
};

console.log(theater.availableTickets); // 100
theater.availableTickets = 50;         // Works!
theater.availableTickets = -10;        // "Can't have negative..."
console.log(theater.availableTickets); // Still 50

Story: The getter is like a helpful guide who tells you how many tickets are left. The setter is like a bouncer who checks your request before letting it through!

Using defineProperty for Getters/Setters

const person = {
  firstName: 'Tony',
  lastName: 'Stark'
};

Object.defineProperty(person, 'fullName', {
  get: function() {
    return `${this.firstName} ${this.lastName}`;
  },
  set: function(name) {
    const parts = name.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  },
  enumerable: true
});

console.log(person.fullName); // 'Tony Stark'
person.fullName = 'Peter Parker';
console.log(person.firstName); // 'Peter'

🧪 Data vs Accessor Descriptors

There are two types of property descriptors:

Data Descriptor (stores a value)

  • value — The actual data
  • writable — Can it change?

Accessor Descriptor (uses functions)

  • get — Function called when reading
  • set — Function called when writing

Important Rule: You can’t mix them! Either use value/writable OR get/set.

// ❌ This causes an ERROR!
Object.defineProperty(obj, 'prop', {
  value: 42,
  get: function() { return 42; }
});
// TypeError: Invalid property descriptor
graph TD A["Property Descriptor"] --> B["Data Descriptor"] A --> C["Accessor Descriptor"] B --> D["value"] B --> E["writable"] C --> F["get function"] C --> G["set function"] B --> H["enumerable"] B --> I["configurable"] C --> J["enumerable"] C --> K["configurable"]

🎓 Practical Example: Protected User Object

Let’s build a user object with all our new knowledge!

function createUser(name, age) {
  const user = {};

  // Public, unchangeable ID
  Object.defineProperty(user, 'id', {
    value: Math.random().toString(36),
    writable: false,
    enumerable: true,
    configurable: false
  });

  // Hidden password
  let _password = '';
  Object.defineProperty(user, 'password', {
    get: () => '********',
    set: (val) => { _password = val; },
    enumerable: false
  });

  // Normal properties
  user.name = name;
  user.age = age;

  return user;
}

const tony = createUser('Tony', 40);
console.log(tony.id);        // Random ID
console.log(tony.password);  // '********'
tony.password = 'ironman';   // Sets secretly
console.log(Object.keys(tony));
// ['id', 'name', 'age'] - no password!

🚀 Quick Summary

Property What It Controls Default
value The stored data undefined
writable Can value change? false*
enumerable Shows in loops? false*
configurable Can rules change? false*
get Function for reading undefined
set Function for writing undefined

*When using defineProperty, defaults are false. Normal assignment defaults to true.


💡 Remember This!

  1. Every property has a hidden control panel (descriptor)
  2. defineProperty is your tool to customize these controls
  3. Getters/Setters let you add logic when reading/writing
  4. Enumerable: false hides from loops and JSON
  5. Configurable: false is permanent—no undo!

You now have the master key to control exactly how your objects behave. Use this power wisely! 🔐

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.