Objects are the core data structure in JavaScript. This article covers various methods and techniques for object manipulation.

Creating Objects

Basic Methods

// Literal (most common)
const obj = { name: 'Alice', age: 25 };

// Constructor
const obj2 = new Object();
obj2.name = 'Bob';

// Object.create (specify prototype)
const proto = { greet() { return `Hello, ${this.name}`; } };
const obj3 = Object.create(proto);
obj3.name = 'Charlie';
obj3.greet();  // 'Hello, Charlie'

// Object.create(null) (no prototype)
const pureObj = Object.create(null);
// pureObj.toString  // undefined

Object.assign

// Merge objects
const target = { a: 1 };
const source = { b: 2, c: 3 };
Object.assign(target, source);
// target = { a: 1, b: 2, c: 3 }

// Merge multiple objects
const merged = Object.assign({}, obj1, obj2, obj3);

// Shallow copy
const copy = Object.assign({}, original);

// Later properties override earlier ones
const defaults = { host: 'localhost', port: 3000 };
const options = { port: 8080 };
const config = Object.assign({}, defaults, options);
// { host: 'localhost', port: 8080 }

// Note: Only copies enumerable own properties

Object.fromEntries

// Create object from key-value pair array
const entries = [['name', 'Alice'], ['age', 25]];
const obj = Object.fromEntries(entries);
// { name: 'Alice', age: 25 }

// Create object from Map
const map = new Map([['a', 1], ['b', 2]]);
const fromMap = Object.fromEntries(map);
// { a: 1, b: 2 }

// Transform object with Object.entries
const original = { a: 1, b: 2, c: 3 };
const doubled = Object.fromEntries(
  Object.entries(original).map(([k, v]) => [k, v * 2])
);
// { a: 2, b: 4, c: 6 }

Property Operations

Property Access

const obj = { name: 'Alice', 'full-name': 'Alice Smith' };

// Dot notation
obj.name;  // 'Alice'

// Bracket notation (supports dynamic keys)
obj['name'];           // 'Alice'
obj['full-name'];      // 'Alice Smith'

const key = 'name';
obj[key];              // 'Alice'

// Optional chaining
const user = { address: { city: 'NYC' } };
user?.address?.city;   // 'NYC'
user?.contact?.email;  // undefined (no error)

Property Checking

const obj = { name: 'Alice', age: 25 };

// in operator (includes inherited)
'name' in obj;       // true
'toString' in obj;   // true

// hasOwnProperty (own properties only)
obj.hasOwnProperty('name');     // true
obj.hasOwnProperty('toString'); // false

// Object.hasOwn (ES2022, recommended)
Object.hasOwn(obj, 'name');     // true
Object.hasOwn(obj, 'toString'); // false

// Checking property value
obj.email !== undefined;  // Unreliable
'email' in obj;           // Reliable

Property Definition

const obj = {};

// Object.defineProperty (single property)
Object.defineProperty(obj, 'name', {
  value: 'Alice',
  writable: true,      // Can be changed
  enumerable: true,    // Shows in enumeration
  configurable: true   // Can be configured
});

// Object.defineProperties (multiple properties)
Object.defineProperties(obj, {
  name: {
    value: 'Alice',
    writable: true
  },
  age: {
    value: 25,
    writable: false  // Read-only
  }
});

// getter and setter
Object.defineProperty(obj, 'fullName', {
  get() {
    return `${this.firstName} ${this.lastName}`;
  },
  set(value) {
    [this.firstName, this.lastName] = value.split(' ');
  }
});

Property Descriptors

const obj = { name: 'Alice' };

// Get single property descriptor
Object.getOwnPropertyDescriptor(obj, 'name');
// { value: 'Alice', writable: true, enumerable: true, configurable: true }

// Get all property descriptors
Object.getOwnPropertyDescriptors(obj);
// { name: { value: 'Alice', writable: true, ... } }

// Complete object copy (including descriptors)
const clone = Object.defineProperties(
  {},
  Object.getOwnPropertyDescriptors(original)
);

Object Traversal

Object.keys / values / entries

const obj = { a: 1, b: 2, c: 3 };

// Get keys array
Object.keys(obj);    // ['a', 'b', 'c']

// Get values array
Object.values(obj);  // [1, 2, 3]

// Get key-value pair array
Object.entries(obj); // [['a', 1], ['b', 2], ['c', 3]]

// Iterate object
Object.entries(obj).forEach(([key, value]) => {
  console.log(`${key}: ${value}`);
});

// Using for...of
for (const [key, value] of Object.entries(obj)) {
  console.log(`${key}: ${value}`);
}

for…in Loop

const obj = { a: 1, b: 2 };

// for...in iteration (includes inherited enumerable)
for (const key in obj) {
  console.log(key, obj[key]);
}

// Iterate own properties only
for (const key in obj) {
  if (Object.hasOwn(obj, key)) {
    console.log(key, obj[key]);
  }
}

Getting Property Names

const sym = Symbol('id');
const obj = {
  name: 'Alice',
  [sym]: 123
};

Object.defineProperty(obj, 'hidden', {
  value: 'secret',
  enumerable: false
});

// Enumerable own properties
Object.keys(obj);                  // ['name']

// All own properties (including non-enumerable)
Object.getOwnPropertyNames(obj);   // ['name', 'hidden']

// Symbol properties
Object.getOwnPropertySymbols(obj); // [Symbol(id)]

// All own properties (string + Symbol)
Reflect.ownKeys(obj);              // ['name', 'hidden', Symbol(id)]

Object Protection

Object.freeze

const obj = { name: 'Alice', address: { city: 'NYC' } };

// Freeze object (no modify, add, delete)
Object.freeze(obj);

obj.name = 'Bob';     // Silently fails (error in strict mode)
obj.age = 25;         // Won't add
delete obj.name;      // Won't delete

console.log(obj.name);  // 'Alice'

// Check if frozen
Object.isFrozen(obj);   // true

// Note: Shallow freeze
obj.address.city = 'LA';  // Still modifiable
console.log(obj.address.city);  // 'LA'

// Deep freeze function
function deepFreeze(obj) {
  Object.freeze(obj);
  Object.values(obj).forEach(value => {
    if (typeof value === 'object' && value !== null) {
      deepFreeze(value);
    }
  });
  return obj;
}

Object.seal

const obj = { name: 'Alice' };

// Seal object (can modify, no add/delete)
Object.seal(obj);

obj.name = 'Bob';     // OK
obj.age = 25;         // Won't add
delete obj.name;      // Won't delete

// Check if sealed
Object.isSealed(obj);  // true

Object.preventExtensions

const obj = { name: 'Alice' };

// Prevent extensions (can modify/delete, no add)
Object.preventExtensions(obj);

obj.name = 'Bob';     // OK
delete obj.name;      // OK
obj.age = 25;         // Won't add

// Check if extensible
Object.isExtensible(obj);  // false

Protection Level Comparison

Object Protection Levels:
┌─────────────────────────────────────────────────────┐
│                                                     │
│   Method              Add   Delete  Modify  Config  │
│   ├── preventExtensions  ✗     ✓       ✓      ✓    │
│   ├── seal              ✗     ✗       ✓      ✗    │
│   └── freeze            ✗     ✗       ✗      ✗    │
│                                                     │
└─────────────────────────────────────────────────────┘

Object Comparison

Equality Checking

// Object.is (strict equality, handles edge cases)
Object.is(1, 1);              // true
Object.is(NaN, NaN);          // true (=== is false)
Object.is(0, -0);             // false (=== is true)

// === strict equality
1 === 1;      // true
NaN === NaN;  // false
0 === -0;     // true

// Object reference comparison
const obj1 = { a: 1 };
const obj2 = { a: 1 };
obj1 === obj2;        // false (different references)

const obj3 = obj1;
obj1 === obj3;        // true (same reference)

Deep Comparison

// Simple deep comparison
function deepEqual(obj1, obj2) {
  if (obj1 === obj2) return true;

  if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
    return false;
  }

  if (obj1 === null || obj2 === null) return false;

  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);

  if (keys1.length !== keys2.length) return false;

  return keys1.every(key =>
    Object.hasOwn(obj2, key) && deepEqual(obj1[key], obj2[key])
  );
}

// Usage
deepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } });  // true

Practical Techniques

Object Destructuring

const user = { name: 'Alice', age: 25, city: 'NYC' };

// Basic destructuring
const { name, age } = user;

// Renaming
const { name: userName } = user;

// Default values
const { country = 'USA' } = user;

// Rest properties
const { name, ...rest } = user;
// rest = { age: 25, city: 'NYC' }

Conditional Properties

const condition = true;

// Conditionally add properties
const obj = {
  always: true,
  ...(condition && { sometimes: true })
};

// Or use ternary
const obj2 = {
  ...(condition ? { a: 1 } : { b: 2 })
};

Dynamic Key Names

const key = 'name';
const value = 'Alice';

// Computed property names
const obj = {
  [key]: value,
  [`${key}Length`]: value.length
};
// { name: 'Alice', nameLength: 5 }

Object Method Shorthand

// ES6 method shorthand
const obj = {
  name: 'Alice',

  // Method shorthand
  greet() {
    return `Hello, ${this.name}`;
  },

  // getter/setter
  get upperName() {
    return this.name.toUpperCase();
  },

  set lowerName(value) {
    this.name = value.toLowerCase();
  }
};

Removing Properties

const obj = { a: 1, b: 2, c: 3 };

// delete operator
delete obj.a;

// Destructuring (doesn't modify original)
const { b, ...rest } = obj;
// rest = { a: 1, c: 3 }

// Filter multiple properties
const omit = (obj, keys) => Object.fromEntries(
  Object.entries(obj).filter(([k]) => !keys.includes(k))
);

omit(obj, ['a', 'b']);  // { c: 3 }

Best Practices Summary

Object Operation Best Practices:
┌─────────────────────────────────────────────────────┐
│                                                     │
│   Creation and Copying                              │
│   ├── Prefer literal syntax for creation           │
│   ├── Use spread operator for shallow copy         │
│   └── Use structuredClone or libraries for deep    │
│                                                     │
│   Property Operations                               │
│   ├── Use Object.hasOwn to check own properties    │
│   ├── Use optional chaining for safe nested access │
│   └── Use destructuring to simplify extraction     │
│                                                     │
│   Traversal Choices                                 │
│   ├── Object.keys/values/entries most common       │
│   ├── Avoid for...in (includes inherited)          │
│   └── Reflect.ownKeys gets all keys                │
│                                                     │
└─────────────────────────────────────────────────────┘
MethodPurposeReturns
Object.keysGet keysString array
Object.valuesGet valuesValue array
Object.entriesGet key-value pairs2D array
Object.assignMerge objectsTarget object

Master object methods to efficiently manipulate JavaScript data structures.