Home/Learn/JavaScript & TypeScript/Objects — Keys, Spread and Immutable Updates

Objects — Keys, Spread and Immutable Updates

Beginner
Working with Data

Objects are the workhorse structure. Reading, iterating and — most importantly — updating them without mutation is the skill that carries straight into React state.

Overview

An object is an unordered set of key-value pairs where keys are strings or symbols. Numeric-looking keys are converted to strings, which explains a few oddities. What matters most in practice is updating: because objects are references, changing one in place can affect code you did not write, and in React it means a component will not re-render because the reference is unchanged. The spread pattern for immutable updates is worth making automatic.

Access, Iteration, and Dynamic Keys

Dot notation for known keys, brackets for computed ones. Object.entries is usually what you want for iteration.

Reading, iterating, computed keys
const user = { name: 'Asha', role: 'student' }

user.name                  // known key
user['role']               // same thing
const key = 'role'
user[key]                  // computed key

Object.keys(user)          // ['name', 'role']
Object.values(user)        // ['Asha', 'student']
Object.entries(user)       // [['name','Asha'], ['role','student']]

for (const [k, v] of Object.entries(user)) { ... }

// Building an object from pairs, and back:
Object.fromEntries([['a', 1], ['b', 2]])   // { a: 1, b: 2 }

// Computed keys in a literal:
const field = 'email'
const patch = { [field]: 'a@b.com', [`${field}Verified`]: false }

Immutable Updates

The pattern you will use in every reducer and every setState. Spread copies one level; nested updates need spreading at each level.

The spread pattern, at each level
const state = {
  user: { name: 'Asha', address: { city: 'Pune' } },
  items: [1, 2],
}

// Top-level field
const next = { ...state, loading: false }

// Nested — spread each level you are changing
const moved = {
  ...state,
  user: {
    ...state.user,
    address: { ...state.user.address, city: 'Mumbai' },
  },
}

// Arrays inside objects
const added = { ...state, items: [...state.items, 3] }
const removed = { ...state, items: state.items.filter(i => i !== 1) }

// Removing a key
const { user, ...withoutUser } = state

Optional Chaining and Safe Reads

API responses are the main source of deeply nested optional data, and these operators exist for exactly that.

Safe reads, merging, reference equality
// Reading what might not be there
const city = response?.data?.user?.address?.city ?? 'Unknown'

// Checking existence properly
'name' in user                    // true, includes inherited
Object.hasOwn(user, 'name')       // true, own properties only
user.name !== undefined           // fails if the key exists with value undefined

// Merging, later wins
const config = { ...defaults, ...userConfig }

// Shallow equality — why React re-renders
const a = { x: 1 }
const b = { x: 1 }
a === b                           // false — different references

Key Points to Remember

  • 1Object.entries() with destructuring is the idiomatic way to iterate key-value pairs
  • 2Spread copies one level deep — nested updates require spreading at every level you change
  • 3Removing a key is done with rest destructuring: const { drop, ...rest } = obj
  • 4Object.hasOwn() checks own properties; the `in` operator also finds inherited ones
  • 5Two objects with identical contents are not ===; this reference comparison is why React re-renders on new objects

Interview Questions

Sign in to ask Aria
1

How do you copy an object in JavaScript? What does "shallow" mean here?

Easy
2

How would you update a deeply nested field without mutating the original object?

Medium
3

Why does { a: 1 } === { a: 1 } evaluate to false?

Easy

Ask Aria about Objects — Keys, Spread and Immutable Updates

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…