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 │
│ │
└─────────────────────────────────────────────────────┘
| Method | Purpose | Returns |
|---|---|---|
| Object.keys | Get keys | String array |
| Object.values | Get values | Value array |
| Object.entries | Get key-value pairs | 2D array |
| Object.assign | Merge objects | Target object |
Master object methods to efficiently manipulate JavaScript data structures.