JavaScript Object Patterns: From Basics to Advanced
Objects are the building blocks of JS. Master them.
Creating Objects
// Literal (most common)
const user = {
name: 'Alex',
age: 30,
greet() { return `Hi, I'm ${this.name}`; }
};
// Object.create (with prototype)
const proto = { greet() { return `Hi, I'm ${this.name}`; } };
const user2 = Object.create(proto);
user2.name = 'Bob';
// Factory function
function createUser(name, age) {
return { name, age, createdAt: new Date() };
}
// Class (ES6+)
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() { return `Hi, I'm ${this.name}`; }
}
Destructuring
const config = { host: 'localhost', port: 3000, ssl: true, timeout: 5000 };
// Basic destructuring
const { host, port } = config;
// With defaults
const { timeout = 10000, retries = 3 } = config;
// Rename while destructuring
const { host: dbHost, port: dbPort } = databaseConfig;
// Nested destructuring
const user = { name: 'Alex', address: { city: 'SF', zip: '94102' } };
const { address: { city, zip } } = user;
// Destructuring in function parameters
function connect({ host = 'localhost', port = 8080, ssl = false }) {
console.log(`Connecting to ${host}:${port} (ssl: ${ssl})`);
}
connect(config);
// Rest pattern
const { name, ...rest } = user; // rest = everything except name
Spreading and Merging
// Spread for shallow clone
const original = { a: 1, b: 2, c: 3 };
const copy = { ...original }; // New object!
// Merge objects
const defaults = { theme: 'dark', lang: 'en', pageSize: 20 };
const userPrefs = { theme: 'light', fontSize: 14 };
const merged = { ...defaults, ...userPrefs };
// { theme: 'light' (overridden!), lang: 'en', pageSize: 20, fontSize: 14 }
// Conditional spread
const optionalFlag = shouldInclude ? { debug: true } : {};
const config = { ...required, ...optionalFlag };
// Array-like to object
const entries = [['name', 'Alex'], ['age', 30]];
const obj = Object.fromEntries(entries); // { name: 'Alex', age: 30 }
Property Access
const obj = { 'user-name': 'Alex', 123: 'numeric key' };
// Dot notation (for valid identifiers)
obj.user; // Works
// Bracket notation (any string)
obj['user-name']; // Required for hyphens
obj[123]; // Required for numeric keys
// Dynamic keys
const field = 'name';
obj[field]; // Same as obj['name']
// Optional chaining (ES2020)
const city = user?.address?.city; // undefined if any is null/undefined
const zip = user?.address?.zip ?? 'N/A'; // Default value
// Nullish coalescing
const timeout = config.timeout ?? 5000; // Only for null/undefined (not 0 or '')
Freezing and Sealing
const settings = { volume: 50, brightness: 70 };
// Prevent adding/deleting properties (can still modify values)
Object.seal(settings);
settings.volume = 80; // ✅ Allowed
settings.theme = 'dark'; // ❌ Ignored (strict mode throws error)
// Completely immutable
Object.freeze(settings);
settings.volume = 90; // ❌ Ignored (strict mode throws error)
// Check
Object.isFrozen(settings); // true
Object.isSealed(settings); // true
Dynamic Property Names
const fields = ['name', 'email', 'age'];
// Create object from array of keys
const obj = {};
fields.forEach(field => {
obj[field] = ''; // Initialize all to empty string
});
// Computed property names
const prefix = 'user_';
const dynamicObj = {
[prefix + 'id']: 123,
[prefix + 'name']: 'Alex',
[`calc_${Date.now()}`]: true,
};
Iterating Over Objects
const scores = { math: 95, english: 87, science: 92 };
// Get keys
Object.keys(scores); // ['math', 'english', 'science']
// Get values
Object.values(scores); // [95, 87, 92]
// Get both (entries)
Object.entries(scores); // [['math', 95], ['english', 87], ['science', 92]]
// Loop over entries
for (const [subject, score] of Object.entries(scores)) {
console.log(`${subject}: ${score}`);
}
// Transform object
const curvedScores = Object.fromEntries(
Object.entries(scores).map(([k, v]) => [k, Math.min(100, v + 5)])
);
// { math: 100, english: 92, science: 97 }
// Filter object
const passing = Object.fromEntries(
Object.entries(scores).filter(([, v]) => v >= 90)
);
// { math: 95, science: 92 }
// Sort object by value
const sorted = Object.fromEntries(
Object.entries(scores).sort(([, a], [, b]) => b - a)
);
// { math: 95, science: 92, english: 87 }
Proxy — Intercept Object Operations
const handler = {
get(target, prop) {
console.log(`Reading: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Setting ${prop} = ${value}`);
target[prop] = value;
return true; // Must return true for success
},
has(target, prop) {
return prop in target || prop.startsWith('_'); // Hide private props
},
deleteProperty(target, prop) {
if (prop.startsWith('_')) return false; // Prevent deleting private
delete target[prop];
return true;
},
};
const proxiedUser = new Proxy({ name: 'Alex', _secret: 'abc' }, handler);
proxiedUser.name; // Logs "Reading: name" → "Alex"
proxiedUser.age = 30; // Logs "Setting age = 30"
'_secret' in proxiedUser; // true (via has trap)
delete proxiedUser._secret; // false (prevented!)
Practical Patterns
Configuration Builder
class ConfigBuilder {
constructor(defaults = {}) {
this.config = { ...defaults };
}
set(key, value) {
this.config[key] = value;
return this; // Chain!
}
setIfAbsent(key, value) {
if (!(key in this.config)) this.config[key] = value;
return this;
}
merge(obj) {
Object.assign(this.config, obj);
return this;
}
build() {
return Object.freeze({ ...this.config });
}
}
const apiConfig = new ConfigBuilder({ baseUrl: '/api' })
.set('timeout', 5000)
.set('retries', 3)
.merge({ headers: { 'Content-Type': 'application/json' } })
.build();
State Manager (Simplified Redux)
function createStore(initialState) {
let state = initialState;
const listeners = new Set();
return {
getState() { return state; },
setState(partial) {
state = { ...state, ...(typeof partial === 'function' ? partial(state) : partial) };
listeners.forEach(fn => fn(state));
},
subscribe(fn) {
listeners.add(fn);
return () => listeners.delete(fn); // Unsubscribe
},
};
}
const store = createStore({ count: 0, loading: false });
store.subscribe(state => console.log('State:', state));
store.setState({ count: store.getState().count + 1 });
What's your favorite object pattern?
Follow @armorbreak for more JavaScript content.
Top comments (0)