Object Methods & Property Descriptors

Difficulty: Intermediate

Question

What are the key Object static methods? Explain property descriptors and immutability in JavaScript objects.

Answer

JavaScript provides several static methods on the Object constructor for working with objects. Object.keys() returns an array of own enumerable property names. Object.values() returns the values. Object.entries() returns key-value pairs as arrays. Object.assign() copies properties from source objects to a target.

Property descriptors control how individual properties behave. Each property has attributes: value, writable (can be changed), enumerable (shows in for...in), and configurable (can be deleted or reconfigured). Object.defineProperty() lets you set these attributes.

For immutability, Object.freeze() makes an object completely immutable (shallow), Object.seal() prevents adding/removing properties but allows modification, and Object.preventExtensions() only prevents adding new properties.

Code examples

Object.keys, values, entries

const user = { name: 'Alice', age: 25, role: 'developer' };

console.log(Object.keys(user));    // property names
console.log(Object.values(user));  // property values
console.log(Object.entries(user)); // [key, value] pairs

// Practical: transform object
const upperEntries = Object.entries(user)
  .map(([key, val]) => [key, String(val).toUpperCase()]);
console.log(Object.fromEntries(upperEntries));

// Check if object has own property
console.log(Object.hasOwn(user, 'name'));  // true
console.log(Object.hasOwn(user, 'toString')); // false

Object.entries and Object.fromEntries let you transform objects using array methods. Object.hasOwn replaces the older hasOwnProperty method.

Property Descriptors

const obj = {};

Object.defineProperty(obj, 'id', {
  value: 42,
  writable: false,     // cannot change value
  enumerable: true,    // shows in Object.keys
  configurable: false  // cannot delete or reconfigure
});

console.log(obj.id);  // 42
obj.id = 100;         // silently fails (strict mode throws)
console.log(obj.id);  // still 42

// Getter/setter descriptors
const person = { _age: 0 };
Object.defineProperty(person, 'age', {
  get() { return this._age; },
  set(val) {
    if (val < 0) throw new Error('Age must be positive');
    this._age = val;
  },
  enumerable: true
});
person.age = 25;
console.log(person.age);
console.log(Object.getOwnPropertyDescriptor(person, 'age'));

Property descriptors give fine-grained control over how properties behave. Getters/setters let you run logic on property access.

freeze, seal, preventExtensions

// Object.freeze - fully immutable (shallow)
const frozen = Object.freeze({ a: 1, b: { c: 2 } });
frozen.a = 10;     // silently fails
frozen.d = 4;      // silently fails
frozen.b.c = 99;   // WORKS! freeze is shallow
console.log(frozen);

// Object.seal - can modify, cannot add/remove
const sealed = Object.seal({ x: 1, y: 2 });
sealed.x = 10;     // works
sealed.z = 3;      // silently fails
delete sealed.x;   // silently fails
console.log(sealed);

// Deep freeze utility
function deepFreeze(obj) {
  Object.freeze(obj);
  Object.values(obj).forEach(val => {
    if (typeof val === 'object' && val !== null) deepFreeze(val);
  });
  return obj;
}
const deep = deepFreeze({ a: { b: { c: 1 } } });
deep.a.b.c = 999;
console.log(deep.a.b.c); // still 1

freeze is shallow - nested objects are still mutable. You need a recursive deepFreeze to make the entire object tree immutable.

Key points

Concepts covered

Object.keys, Object.entries, Property Descriptors, Object.freeze, Computed Properties, Object.assign