JavaScript Objects

Objects are the foundation of JavaScript. Learn how to create, access, destructure, and merge objects - plus all the modern syntax that makes working with objects clean and expressive.

Beginner 11 min read 12 examples

Object Basics

An object is a collection of key-value pairs. Keys are strings (or Symbols); values can be any type, including other objects and functions.

JavaScript
// Object literal
const user = {
    name: "Alice",
    age: 28,
    isAdmin: false,
    address: {
        city: "London",
        zip: "SW1A 1AA"
    }
};

// Read properties - dot notation (preferred)
console.log(user.name);          // "Alice"
console.log(user.address.city);  // "London"

// Bracket notation - use when key is dynamic or has spaces
console.log(user["age"]);        // 28
const key = "name";
console.log(user[key]);          // "Alice"
const data = { "first name": "Bob" };
console.log(data["first name"]); // "Bob"

// Set and update properties
user.email = "alice@example.com"; // add
user.age   = 29;                  // update
delete user.isAdmin;              // remove

console.log(user);

// Check property existence
console.log("name" in user);           // true
console.log(Object.hasOwn(user, "age")); // true (ES2022)
console.log(user.phone !== undefined); // false (but unsafe if prop is undefined intentionally)

Property Shorthand and Computed Keys

ES6 introduced shorthand syntax that eliminates repetitive code when building objects:

JavaScript
const name = "Alice";
const age  = 28;

// Property shorthand: when variable name matches key name
const user = { name, age };  // same as { name: name, age: age }
console.log(user); // { name: "Alice", age: 28 }

// Method shorthand
const calculator = {
    value: 0,
    add(n) { this.value += n; return this; },  // shorthand method
    subtract(n) { this.value -= n; return this; },
    result() { return this.value; }
};
calculator.add(10).add(5).subtract(3);
console.log(calculator.result()); // 12

// Computed property names - dynamic keys
const field = "email";
const contact = {
    [field]: "alice@example.com",   // key = value of 'field'
    [`${field}_verified`]: true,    // template literal key
};
console.log(contact.email);          // "alice@example.com"
console.log(contact.email_verified); // true

// Building dynamic objects with computed keys
function makeField(name, value) {
    return { [name]: value };
}
console.log(makeField("score", 100)); // { score: 100 }

Destructuring

Destructuring extracts properties from an object into named variables in a single line.

JavaScript
const user = { name: "Alice", age: 28, city: "London" };

// Basic destructuring
const { name, age } = user;
console.log(name); // "Alice"
console.log(age);  // 28

// Rename while destructuring
const { name: userName, age: userAge } = user;
console.log(userName); // "Alice"

// Default values
const { name: n, role = "user" } = user;
console.log(role); // "user" (not on user object, so default kicks in)

// Nested destructuring
const config = { db: { host: "localhost", port: 5432 } };
const { db: { host, port } } = config;
console.log(host); // "localhost"
console.log(port); // 5432

// Rest in destructuring
const { name: name2, ...rest } = user;
console.log(name2);  // "Alice"
console.log(rest);   // { age: 28, city: "London" }

// Destructuring in function parameters
function greet({ name, age = 0 }) {
    return `${name} is ${age} years old`;
}
console.log(greet(user)); // "Alice is 28 years old"

Spread and Merge

The spread operator ... creates a shallow copy or merges objects. Later properties overwrite earlier ones.

JavaScript
const defaults = { theme: "light", language: "en", fontSize: 16 };
const userPrefs = { theme: "dark", fontSize: 18 };

// Merge: right side overwrites duplicates
const settings = { ...defaults, ...userPrefs };
console.log(settings);
// { theme: "dark", language: "en", fontSize: 18 }

// Shallow copy (modifying copy does not affect original)
const copy = { ...defaults };
copy.theme = "system";
console.log(defaults.theme); // "light" - original unchanged

// Add/override while copying
const updated = { ...defaults, theme: "dark", newProp: true };

// Deep copy - use structuredClone (ES2022)
const nested = { a: 1, b: { c: 2 } };
const shallow = { ...nested };
shallow.b.c = 99;             // modifies the original!
console.log(nested.b.c);      // 99 (shared reference!)

const deep = structuredClone(nested); // true deep copy
deep.b.c = 999;
console.log(nested.b.c);     // 2 - original intact
Spread Creates Shallow Copies

When you spread an object, nested objects are still shared by reference. If you modify copy.nestedObj.property, it also modifies original.nestedObj.property. For a true independent clone, use structuredClone(obj) (modern) or JSON.parse(JSON.stringify(obj)) (older - does not handle Date, undefined, functions).

Optional Chaining on Objects

JavaScript
const user = {
    name: "Alice",
    address: {
        city: "London"
    }
};

// Without optional chaining - crashes if address is missing
// const zip = user.location.zip; // TypeError!

// With optional chaining (?.)
console.log(user?.address?.city);   // "London"
console.log(user?.phone?.number);   // undefined (no crash)

// On methods
console.log(user?.getFullName?.());  // undefined (method does not exist)

// Combined with nullish coalescing for defaults
const city = user?.address?.city ?? "Unknown";
console.log(city); // "London"

const zip = user?.address?.zip ?? "N/A";
console.log(zip);  // "N/A"

Object Static Methods

JavaScript
const user = { name: "Alice", age: 28, city: "London" };

// Object.keys - array of property names
console.log(Object.keys(user));    // ["name","age","city"]

// Object.values - array of property values
console.log(Object.values(user));  // ["Alice",28,"London"]

// Object.entries - array of [key, value] pairs
console.log(Object.entries(user));
// [["name","Alice"],["age",28],["city","London"]]

// Iterate entries
for (const [key, value] of Object.entries(user)) {
    console.log(`${key}: ${value}`);
}

// Object.fromEntries - convert entries back to object
const doubled = Object.fromEntries(
    Object.entries({ a: 1, b: 2, c: 3 }).map(([k, v]) => [k, v * 2])
);
console.log(doubled); // { a: 2, b: 4, c: 6 }

// Object.assign - copy properties (mutates target)
const target = { a: 1 };
Object.assign(target, { b: 2 }, { c: 3 });
console.log(target); // { a: 1, b: 2, c: 3 }

// Object.freeze - prevent modification
const config = Object.freeze({ PORT: 3000, HOST: "localhost" });
config.PORT = 9999; // silently ignored
console.log(config.PORT); // 3000

Nested Objects and Arrays of Objects

JavaScript
const company = {
    name: "RubanTech",
    employees: [
        { id: 1, name: "Alice", dept: "Engineering", skills: ["JS","Python"] },
        { id: 2, name: "Bob",   dept: "Design",      skills: ["Figma","CSS"] },
        { id: 3, name: "Carol", dept: "Engineering", skills: ["JS","Go"] },
    ]
};

// Access nested data
console.log(company.employees[0].name);       // "Alice"
console.log(company.employees[0].skills[1]);  // "Python"

// Filter and map nested arrays
const engineers = company.employees
    .filter(e => e.dept === "Engineering")
    .map(e => e.name);
console.log(engineers); // ["Alice","Carol"]

// Find and destructure
const { name: empName, skills } = company.employees.find(e => e.id === 1);
console.log(empName);  // "Alice"
console.log(skills);   // ["JS","Python"]

Common Object Patterns

JavaScript
// 1. Options object pattern (named parameters)
function createUser({ name, age = 0, role = "user" } = {}) {
    return { name, age, role, createdAt: new Date() };
}
const user = createUser({ name: "Alice", age: 28 });

// 2. Immutable update pattern (React / Redux style)
const state = { count: 0, user: "Alice" };
const newState = { ...state, count: state.count + 1 };

// 3. Lookup table (replaces long if/else chains)
const statusLabels = {
    200: "OK",
    404: "Not Found",
    500: "Internal Server Error",
};
const label = statusLabels[404] ?? "Unknown";
console.log(label); // "Not Found"

// 4. Object as namespace
const MathUtils = {
    square: x => x ** 2,
    cube:   x => x ** 3,
    clamp: (n, min, max) => Math.min(Math.max(n, min), max)
};
console.log(MathUtils.square(5)); // 25

Frequently Asked Questions

Primitives (numbers, strings, booleans) are copied by value: let b = a creates a separate copy. Objects are copied by reference: let b = a makes b point to the same object. Modifying b also modifies a. To create a true independent copy, use structuredClone(obj) (deep copy) or {...obj} (shallow copy - nested objects are still shared).

Use the spread operator: const merged = { ...obj1, ...obj2 }. Properties from the right side overwrite duplicates from the left. Alternatively use Object.assign({}, obj1, obj2). Both are shallow merges - nested objects are not deeply merged. For deep merge, use a library like lodash's _.merge() or structuredClone.

Use Object.hasOwn(obj, key) (ES2022) or the older obj.hasOwnProperty(key). Avoid just checking obj.key !== undefined - that fails if the property exists but is explicitly set to undefined. The in operator checks for the property including inherited ones: "key" in obj.

Object.freeze(obj) prevents adding, removing, or changing properties on the object. It is shallow - nested objects are not frozen. This is useful for creating true constants: const CONFIG = Object.freeze({ API_URL: "...", TIMEOUT: 5000 }). Attempting to modify a frozen object silently fails in non-strict mode or throws a TypeError in strict mode.