Skip to content
C Codeloom
JavaScript

JavaScript Objects: The Most Useful Data Structure

A practical guide to JavaScript objects — creation, access, mutation, iteration, shorthand syntax, destructuring, spread, and how object references actually work.

·9 min read · By Yash Kesharwani
Beginner 11 min read

What you'll learn

  • How to create objects and access their properties
  • Dot notation vs. bracket notation
  • Iterating with Object.keys, values, and entries
  • Shorthand syntax, computed keys, and destructuring
  • Spread for merging and copying objects
  • Why objects are passed by reference and what that means

Prerequisites

  • JavaScript arrays — see Arrays

An object is an unordered collection of key/value pairs. Almost every meaningful data structure in JavaScript — a user record, an API response, a configuration, the document model in a browser — is an object. Mastering them is the single biggest leap from “I can write a script” to “I can build software”.

Creating an object

The most common way is with curly braces — the object literal:

const user = {
  name: "Ada Lovelace",
  age: 36,
  isAdmin: true,
};

Keys are strings (or symbols). Values can be anything: numbers, strings, booleans, arrays, other objects, even functions.

const profile = {
  name: "Ada",
  hobbies: ["mathematics", "writing"],
  address: {
    city: "London",
    country: "UK",
  },
  greet() {
    return `Hello, I'm ${this.name}`;
  },
};

An empty object:

const empty = {};

Accessing properties

Two ways to read or write a property: dot notation and bracket notation.

const user = { name: "Ada", age: 36 };

console.log(user.name);       // 'Ada'
console.log(user["age"]);     // 36

user.age = 37;                // update
user.email = "ada@example.com";  // add a new property
console.log(user);
// { name: 'Ada', age: 37, email: 'ada@example.com' }

Dot notation is the default — it’s shorter and reads better. Use bracket notation when:

  • The key isn’t a valid identifier (contains spaces or starts with a digit):

    const grades = { "math 101": 92 };
    console.log(grades["math 101"]);
  • The key is held in a variable:

    const key = "name";
    console.log(user[key]);

Reading a missing property returns undefined, not an error:

console.log(user.zipCode);    // undefined

To delete a property:

delete user.email;
console.log(user);   // { name: 'Ada', age: 37 }

Checking what’s in an object

const user = { name: "Ada", age: 36 };

"name" in user;                          // true
"email" in user;                         // false
Object.hasOwn(user, "name");             // true   — modern, preferred
user.email !== undefined;                // common shortcut

Object.hasOwn (Node 16.9+, modern browsers) is the recommended way — it correctly ignores inherited properties.

Shorthand syntax

When a property name matches the variable name you’re assigning from, you can write it once:

const name = "Ada";
const age = 36;

// Long form
const user1 = { name: name, age: age };

// Shorthand — same result
const user2 = { name, age };

Method shorthand drops the function keyword and ::

const calculator = {
  add(a, b) { return a + b; },
  subtract(a, b) { return a - b; },
};
console.log(calculator.add(2, 3));    // 5

Computed keys let you build a key from an expression:

const key = "score";
const i = 3;

const result = {
  [key]: 100,
  [`item_${i}`]: "third",
};
console.log(result);   // { score: 100, item_3: 'third' }

Iterating over an object

Three companion methods on Object give you the keys, the values, or both:

const user = { name: "Ada", age: 36, isAdmin: true };

Object.keys(user);    // ['name', 'age', 'isAdmin']
Object.values(user);  // ['Ada', 36, true]
Object.entries(user); // [['name', 'Ada'], ['age', 36], ['isAdmin', true]]

The cleanest iteration uses for...of with entries() and destructuring:

for (const [key, value] of Object.entries(user)) {
  console.log(`${key}: ${value}`);
}
// name: Ada
// age: 36
// isAdmin: true

This pattern shows up constantly in real code — config parsing, form handling, data display.

for...in also works but iterates inherited properties too, which is rarely what you want:

for (const key in user) {
  console.log(key, user[key]);
}

Stick with Object.entries + for...of.

Try it yourself. Given this object, write a loop that prints "X scored Y" for each entry, and computes the average score:

const scores = { Alice: 82, Bob: 47, Carol: 91, Dave: 56 };

Hint: use Object.entries for the loop, and Object.values plus reduce for the average.

Destructuring

Pulling properties out of objects into variables is one of the most-used modern features.

const user = { name: "Ada", age: 36, city: "London" };

const { name, age } = user;
console.log(name, age);          // 'Ada' 36

You can rename:

const { name: fullName } = user;
console.log(fullName);           // 'Ada'

Provide a default for missing properties:

const { country = "UK" } = user;
console.log(country);            // 'UK'  — used because user.country is undefined

And collect the rest:

const { name: n, ...other } = user;
console.log(n);       // 'Ada'
console.log(other);   // { age: 36, city: 'London' }

Destructuring shows up especially often in function parameters:

function greet({ name, age }) {
  return `Hello ${name}, age ${age}.`;
}

console.log(greet(user));   // 'Hello Ada, age 36.'

This pattern is the JavaScript equivalent of named arguments. It is everywhere in modern React, Node, and library code.

Spread for copying and merging

The ... operator on objects copies properties into a new object:

const user = { name: "Ada", age: 36 };
const copy = { ...user };
console.log(copy);    // { name: 'Ada', age: 36 }

To create a modified copy without touching the original:

const updated = { ...user, age: 37 };
console.log(user);    // { name: 'Ada', age: 36 }   — unchanged
console.log(updated); // { name: 'Ada', age: 37 }

Later properties override earlier ones, which makes merging easy:

const defaults = { theme: "light", fontSize: 14, showSidebar: true };
const userPrefs = { fontSize: 16 };

const settings = { ...defaults, ...userPrefs };
console.log(settings);
// { theme: 'light', fontSize: 16, showSidebar: true }

This “immutable update” pattern is fundamental in modern JavaScript, especially in React.

Objects are passed by reference

This is the single most important thing to understand about objects.

A variable holding an object stores a reference to it, not a copy. Assigning the variable copies the reference, so both names point to the same underlying object:

const a = { count: 1 };
const b = a;
b.count = 5;
console.log(a.count);    // 5   — surprise

Equality compares references, not contents:

const a = { name: "Ada" };
const b = { name: "Ada" };
console.log(a === b);    // false  — different objects
console.log(a === a);    // true

The same applies when passing objects to functions:

function birthday(person) {
  person.age += 1;
}

const ada = { name: "Ada", age: 36 };
birthday(ada);
console.log(ada.age);    // 37   — the original was modified

If you want a function to not modify its argument, make a copy first or use spread to produce a new object:

function withBirthday(person) {
  return { ...person, age: person.age + 1 };
}

const ada = { name: "Ada", age: 36 };
const olderAda = withBirthday(ada);
console.log(ada.age);       // 36   — unchanged
console.log(olderAda.age);  // 37

This explains why const doesn’t make objects “constant” — the variable can’t be reassigned, but the object it points to is still mutable.

Try it yourself. Predict the output:

const a = { value: 1 };
const b = a;
const c = { ...a };
b.value = 2;
c.value = 3;
console.log(a.value, b.value, c.value);

The first two share a reference; the third was copied with spread.

Useful Object methods

A few utility functions on Object itself worth knowing.

Object.assign — older form of spread merging

const merged = Object.assign({}, defaults, userPrefs);

Spread ({...a, ...b}) is now preferred, but you’ll see Object.assign in older code.

Object.freeze — make an object immutable

const config = Object.freeze({ apiUrl: "https://api.example.com" });
config.apiUrl = "other";    // silently ignored (or throws in strict mode)
console.log(config.apiUrl); // 'https://api.example.com'

Useful for configuration objects you never want to change.

structuredClone — deep copy

const original = { name: "Ada", address: { city: "London" } };
const deep = structuredClone(original);
deep.address.city = "Paris";
console.log(original.address.city);   // 'London'  — unaffected

Spread is shallow — the inner address object is still shared. structuredClone (Node 17+, modern browsers) handles nested data correctly.

Objects as maps (and the new Map)

Objects can be used as quick key/value stores:

const counts = {};
counts.apple = (counts.apple ?? 0) + 1;
counts.apple = (counts.apple ?? 0) + 1;
counts.banana = (counts.banana ?? 0) + 1;
console.log(counts);   // { apple: 2, banana: 1 }

For larger or more dynamic key/value work, JavaScript has a dedicated Map type that allows any kind of key, preserves insertion order, and has a clean API:

const m = new Map();
m.set("apple", 2);
m.set("banana", 1);
console.log(m.get("apple"));   // 2
console.log(m.has("cherry"));  // false
console.log(m.size);           // 2

We mention Map for completeness — for beginner-level work, plain objects are usually fine.

A small worked example

A function that takes a list of orders and returns a summary object showing totals per customer.

const orders = [
  { customer: "Ada", total: 42.50 },
  { customer: "Linus", total: 18.00 },
  { customer: "Ada", total: 19.99 },
  { customer: "Grace", total: 99.00 },
  { customer: "Linus", total: 5.50 },
];

const totals = orders.reduce((acc, order) => {
  acc[order.customer] = (acc[order.customer] ?? 0) + order.total;
  return acc;
}, {});

console.log(totals);
// { Ada: 62.49, Linus: 23.5, Grace: 99 }

for (const [name, sum] of Object.entries(totals)) {
  console.log(`${name}: $${sum.toFixed(2)}`);
}

Output:

Ada: $62.49
Linus: $23.50
Grace: $99.00

This is the standard texture of real-world JavaScript work: reduce a list of records into an object keyed by something interesting, then iterate it for display.

Recap

You now know:

  • Objects are unordered collections of key/value pairs created with {}
  • Dot notation for known keys, bracket notation for dynamic or unusual keys
  • Object.keys, Object.values, Object.entries for iteration
  • Shorthand property syntax, method shorthand, and computed keys
  • Destructuring to unpack into variables, including with defaults and rest
  • Spread to copy and merge: { ...a, ...b } is the everyday pattern
  • Objects are passed by reference — that’s why const doesn’t make them immutable
  • Object.freeze for true immutability; structuredClone for deep copies

Next steps

You now have variables, types, numbers, strings, arrays, and objects. The remaining building blocks are the operators that combine them — and the conditionals and loops that bring them to life.

→ Next: JavaScript Operators and Comparisons (==, ===, &&, ||)

Questions or feedback? Email codeloomdevv@gmail.com.