Object Properties Configuration Β· Astro Tech Blog

Object Properties Configuration

Property Flags and Descriptors

Object properties have more than just a value. Each property has three flags:

  • writable β€” can the value be changed?
  • enumerable β€” does it show up in for...in loops?
  • configurable β€” can the property be deleted or modified?

Property Descriptor

let user = { name: "Alice" };

let descriptor = Object.getOwnPropertyDescriptor(user, "name");
console.log(descriptor);
// β†’ { value: "Alice", writable: true, enumerable: true, configurable: true }

Defining Properties with Flags

let user = {};

Object.defineProperty(user, "name", {
  value: "Alice",
  writable: false,   // Can't change
  enumerable: false, // Won't appear in loops
  configurable: false // Can't delete or reconfigure
});

user.name = "Bob";     // Fails silently (in strict mode: TypeError)
console.log(user.name); // β†’ "Alice"

for (let key in user) console.log(key); // Nothing β€” not enumerable

delete user.name;        // Fails silently
console.log(user.name);  // β†’ "Alice"

Non-writable

let user = {};
Object.defineProperty(user, "id", {
  value: 42,
  writable: false // Read-only
});

user.id = 100; // Silently ignored (or TypeError in strict)
console.log(user.id); // β†’ 42

Non-enumerable

let user = {
  name: "Alice",
  age: 30
};

// Make a property hidden from loops
Object.defineProperty(user, "internalId", {
  value: "SECRET",
  enumerable: false
});

for (let key in user) console.log(key);
// β†’ "name", "age" (internalId is hidden)

console.log(Object.keys(user)); // β†’ ["name", "age"]
console.log(user.internalId);   // β†’ "SECRET" (still accessible directly)

Non-configurable

A non-configurable property can’t be deleted or redefined:

let user = {};
Object.defineProperty(user, "name", {
  value: "Alice",
  configurable: false
});

delete user.name;     // Silently ignored
// Object.defineProperty(user, "name", { writable: false }); // TypeError!

Object.defineProperties (multiple at once)

let user = {};

Object.defineProperties(user, {
  name: { value: "Alice", writable: true, enumerable: true },
  age: { value: 30, writable: false, enumerable: true },
  id: { value: 42, writable: false, configurable: false }
});

Object.getOwnPropertyDescriptors

Get all property descriptors at once:

let user = { name: "Alice", age: 30 };

let descriptors = Object.getOwnPropertyDescriptors(user);
console.log(descriptors);
// β†’ {
//   name: { value: "Alice", writable: true, enumerable: true, configurable: true },
//   age: { value: 30, writable: true, enumerable: true, configurable: true }
// }

Sealing and Freezing Objects

let user = { name: "Alice", age: 30 };

// Seal β€” prevent adding/removing properties (sets configurable: false)
Object.seal(user);
delete user.age;     // Silently ignored
user.email = "test"; // Silently ignored (can't add)
user.name = "Bob";   // βœ… Allowed (writable)

// Freeze β€” make completely immutable
Object.freeze(user);
user.name = "Charlie"; // Silently ignored
delete user.age;       // Silently ignored

// Check state
Object.isSealed(user);  // β†’ true
Object.isFrozen(user);  // β†’ true
Object.isExtensible(user); // β†’ false
MethodAddDeleteWriteReconfigure
seal()βŒβŒβœ…βŒ
freeze()❌❌❌❌
preventExtensions()βŒβœ…βœ…βœ…

Property Getters and Setters

Accessor properties are functions that look like regular properties.

Getter and Setter Syntax

let user = {
  firstName: "Alice",
  lastName: "Johnson",

  // Getter
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  },

  // Setter
  set fullName(value) {
    [this.firstName, this.lastName] = value.split(" ");
  }
};

console.log(user.fullName); // β†’ "Alice Johnson"
user.fullName = "Bob Smith";
console.log(user.firstName); // β†’ "Bob"
console.log(user.lastName);  // β†’ "Smith"

Why use getters/setters?

  • Computed properties β€” derive a value from other properties
  • Validation β€” check values before setting
  • Encapsulation β€” hide internal representation

Validation with Setter

let user = {
  _age: 0, // _ prefix by convention means "internal"

  get age() {
    return this._age;
  },

  set age(value) {
    if (value < 0 || value > 150) {
      throw new Error("Invalid age");
    }
    this._age = value;
  }
};

user.age = 25;
console.log(user.age); // β†’ 25

// user.age = -5; // β†’ Error: Invalid age

Using defineProperty for accessors

let user = { firstName: "Alice", lastName: "Johnson" };

Object.defineProperty(user, "fullName", {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
  set(value) {
    [this.firstName, this.lastName] = value.split(" ");
  },
  enumerable: true,
  configurable: true
});

console.log(user.fullName); // β†’ "Alice Johnson"

Getter/Setter Descriptors

Accessor descriptors don’t have value or writable:

let descriptor = Object.getOwnPropertyDescriptor(user, "fullName");
console.log(descriptor);
// β†’ { get: [Function], set: [Function], enumerable: true, configurable: true }

Smart Getter β€” Lazy Computation

let data = {
  _cache: null,

  get bigData() {
    if (!this._cache) {
      console.log("Computing...");
      this._cache = heavyComputation();
    }
    return this._cache;
  }
};

console.log(data.bigData); // β†’ "Computing..." then result
console.log(data.bigData); // β†’ From cache (no computation)

Compatibility with Getters/Setters

Getters/setters let you add logic without breaking existing code:

// Before β€” simple property
let user = { name: "Alice" };

// After β€” refactored to getter (code using user.name still works)
let user = {
  _name: "Alice",
  get name() { return this._name; },
  set name(value) {
    if (value.length < 2) throw new Error("Name too short");
    this._name = value;
  }
};
Demo: Getters and Setters Demo
JavaScript
let user = {
_name: 'Alice',
_age: 30,

get name() {
return this._name;
},

set name(value) {
if (!value) throw new Error('Name required');
this._name = value;
},

get age() {
return this._age;
},

set age(value) {
if (value < 0 || value > 150) throw new Error('Invalid age');
this._age = value;
},

get isAdult() {
return this._age >= 18;
},

get description() {
return this._name + ' is ' + this._age + ' years old';
}
};

document.write('Name: ' + user.name + '<br>');
document.write('Age: ' + user.age + '<br>');
document.write('Adult: ' + user.isAdult + '<br>');
document.write('Description: ' + user.description + '<br>');

// Try setting
user.name = 'Bob';
user.age = 25;
document.write('<br>After update:<br>');
document.write('Name: ' + user.name + '<br>');
document.write('Description: ' + user.description + '<br>');
Live Output Window