DEV Community

Alex Chen
Alex Chen

Posted on

JavaScript Object Patterns: From Basics to Advanced

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}`; }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode

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 '')
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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,
};
Enter fullscreen mode Exit fullscreen mode

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 }
Enter fullscreen mode Exit fullscreen mode

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!)
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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 });
Enter fullscreen mode Exit fullscreen mode

What's your favorite object pattern?

Follow @armorbreak for more JavaScript content.

Top comments (0)