Object Methods

Difficulty: Intermediate

JavaScript objects are the fundamental building blocks of the language, and the static methods on the `Object` constructor give you powerful tools for inspecting, copying, locking down, and creating objects. Understanding these methods is essential for writing clean, predictable JavaScript - they come up in everything from state management to API data processing to framework internals.

The inspection trio - `Object.keys`, `Object.values`, and `Object.entries` - let you convert an object's own enumerable properties into arrays. `Object.keys` returns an array of property name strings, `Object.values` returns an array of the corresponding values, and `Object.entries` returns an array of [key, value] pairs. These are indispensable when you need to iterate over object properties, transform object shapes, or convert between objects and other data structures like Maps.

`Object.assign` performs a shallow copy of all enumerable own properties from one or more source objects to a target object. It is commonly used to merge objects, create copies with overrides, or set default values. However, it only copies property values - if a value is a reference to another object, both the original and the copy will point to the same nested object. This is a critical detail that trips up many developers.

`Object.freeze` and `Object.seal` provide different levels of immutability. `Object.freeze` makes an object completely immutable - you cannot add, remove, or modify any properties. `Object.seal` is less strict: you cannot add or remove properties, but you can still modify the values of existing properties. Both operate shallowly, meaning nested objects are not frozen or sealed unless you explicitly do so recursively.

`Object.create` creates a new object with a specified prototype, giving you fine-grained control over the prototype chain without using constructor functions or classes. `Object.defineProperty` lets you add or modify a single property with full control over its property descriptor - you can set whether it is writable, enumerable, and configurable. Together, these methods expose the low-level machinery of JavaScript's object system, which is exactly what framework authors and library developers use under the hood.

Code examples

Object.keys, Object.values, and Object.entries

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

console.log(Object.keys(user));
console.log(Object.values(user));
console.log(Object.entries(user));

// Practical: transform object to query string
const params = { page: 1, limit: 20, sort: 'name' };
const queryString = Object.entries(params)
  .map(([key, val]) => `${key}=${val}`)
  .join('&');

console.log(queryString);

Object.keys returns the property names, Object.values returns the values, and Object.entries returns [key, value] tuples. The query string example shows a real-world use case - converting a params object into a URL query string by mapping entries to key=value pairs and joining with &.

Object.assign - merging and shallow copying

// Merge objects (later sources override earlier ones)
const defaults = { theme: 'light', lang: 'en', fontSize: 14 };
const userPrefs = { theme: 'dark', fontSize: 18 };

const config = Object.assign({}, defaults, userPrefs);
console.log(config);

// Shallow copy gotcha
const original = { name: 'Alice', address: { city: 'NYC' } };
const copy = Object.assign({}, original);
copy.address.city = 'LA';

console.log(original.address.city); // also changed!

Object.assign copies properties from source objects to the target. Properties from userPrefs override those from defaults. The shallow copy example demonstrates a common trap - the address object is shared by reference, so modifying copy.address.city also changes the original.

Object.freeze vs Object.seal

// freeze: fully immutable (shallow)
const frozen = Object.freeze({ x: 1, y: 2, nested: { z: 3 } });
frozen.x = 99;          // silently fails (throws in strict mode)
frozen.newProp = 'hi';  // silently fails
console.log(frozen.x);  // still 1

// but nested objects are NOT frozen
frozen.nested.z = 999;
console.log(frozen.nested.z); // changed!

// seal: can modify existing, cannot add/remove
const sealed = Object.seal({ a: 1, b: 2 });
sealed.a = 100;        // allowed
sealed.c = 3;          // silently fails
delete sealed.b;       // silently fails
console.log(sealed);

Object.freeze prevents all modifications to direct properties, but nested objects remain mutable - freeze is shallow. Object.seal allows value changes to existing properties but blocks additions and deletions. In strict mode, violations throw TypeErrors instead of silently failing.

Object.create and Object.defineProperty

// Object.create: set up prototype chain
const animal = {
  speak() { return `${this.name} makes a sound`; }
};

const dog = Object.create(animal);
dog.name = 'Rex';
console.log(dog.speak());
console.log(Object.getPrototypeOf(dog) === animal);

// Object.defineProperty: fine-grained control
const person = {};
Object.defineProperty(person, 'ssn', {
  value: '123-45-6789',
  writable: false,
  enumerable: false,
  configurable: false
});

console.log(person.ssn);
console.log(Object.keys(person)); // ssn is not enumerable
person.ssn = 'xxx';               // fails silently
console.log(person.ssn);           // unchanged

Object.create creates dog with animal as its prototype, so dog inherits the speak method. Object.defineProperty creates a non-writable, non-enumerable, non-configurable property - it won't show up in Object.keys, can't be changed, and can't be reconfigured or deleted.

Key points

Concepts covered

Object.keys, Object.values, Object.entries, Object.assign, Object.freeze, Object.seal, Object.create, Object.defineProperty, property descriptors