Objects are JavaScript's way of grouping related data and functionality together. While arrays store ordered lists of values, objects store named properties with their values like a real-world object with its characteristics.
// An array stores ordered values
const person = ['Alice', 28, 'Amsterdam']; // What does each value mean?
// An object stores named properties
const person = {
name: 'Alice',
age: 28,
city: 'Amsterdam'
}; // Clear and self-documenting
Objects are everywhere in JavaScript. They represent users, products, configuration settings, API responses, or any data structure where you need to organize related information with meaningful labels. They're one of the most fundamental and powerful features of the language.
<aside> 💡
Think of objects as containers with labeled compartments. Each label (key) points to a value, making your data self-documenting and easy to work with.
</aside>
https://www.youtube.com/watch?v=lo7o91qLzxc
The most common way to create objects is using object literal syntax with curly braces. Properties are written as key: value pairs, separated by commas. Keys are usually written without quotes (unless they contain special characters or spaces).
// Empty object
const empty = {};
// Object with properties
const user = {
name: 'Bob',
age: 25,
isActive: true
};
// Values can be any type
const mixed = {
text: 'hello',
number: 42,
boolean: true,
array: [1, 2, 3],
nested: { key: 'value' },
nothing: null
};
// Keys with special characters need quotes
const settings = {
'background-color': 'blue',
'font-size': '16px',
normalKey: 'value'
};
<aside> ⌨️
Create an object representing your favorite movie with properties: title, director, year, and rating. Log it to the console.
</aside>
There are two ways to access object properties: dot notation and bracket notation.
This is the most common notation for accessing object properties. It’s clean and readable, so use it whenever possible.
const user = {
name: 'Alice',
age: 28,
email: '[email protected]'
};
console.log(user.name); // 'Alice'
console.log(user.age); // 28
console.log(user.email); // '[email protected]'
// Accessing non-existent property
console.log(user.phone); // undefined
This form of notation is used for dynamically accessing object properties. Use bracket notation when property names have special characters, spaces, or when you need to access properties dynamically using variables.
const user = {
name: 'Alice',
age: 28,
'favorite color': 'blue'
};
// Access with brackets
console.log(user['name']); // 'Alice'
console.log(user['favorite color']); // 'blue' (spaces require brackets)
// Dynamic property access
const property = 'age';
console.log(user[property]); // 28
// Computed property names
const key = 'email';
console.log(user[key]); // undefined (property doesn't exist)
<aside> ⚠️
Accessing a non-existent property returns undefined, not an error. Always check if a property exists before using it in critical code.
</aside>
You can mutate objects: add, update, or delete after creation.
const user = {
name: 'Alice',
age: 28
};
// Update existing property
user.age = 29;
console.log(user.age); // 29
// Add new property
user.email = '[email protected]';
console.log(user); // { name: 'Alice', age: 29, email: '[email protected]' }
// Add with bracket notation
user['isActive'] = true;
// Delete property
delete user.age;
console.log(user); // { name: 'Alice', email: '[email protected]', isActive: true }
<aside> ⌨️
Create an empty object called car. Add properties for brand, model, and year. Then update the year and add a color property. Finally, delete the year property.
</aside>
Objects can contain other objects, creating hierarchical data structures:
const user = {
name: 'Alice',
age: 28,
address: {
street: 'Main Street 123',
city: 'Amsterdam',
country: 'Netherlands'
},
preferences: {
theme: 'dark',
language: 'en',
notifications: {
email: true,
push: false
}
}
};
// Access nested properties
console.log(user.address.city); // 'Amsterdam'
console.log(user.preferences.theme); // 'dark'
console.log(user.preferences.notifications.email); // true
// Bracket notation with nested objects
console.log(user['address']['street']); // 'Main Street 123'
// Typical API response structure
const response = {
status: 'success',
data: {
user: {
id: 123,
profile: {
name: 'Alice',
avatar: 'avatar.jpg'
}
},
posts: [
{ id: 1, title: 'First Post' },
{ id: 2, title: 'Second Post' }
]
}
};
console.log(response.data.user.profile.name); // 'Alice'
<aside> ⚠️
Be careful when accessing deeply nested properties. If any intermediate property is undefined, you'll get an error. Check each level or use optional chaining (?.) when available.
</aside>
const user = {
name: 'Bob'
// No address property
};
// This will cause an error!
// console.log(user.address.city); // Error: Cannot read property 'city' of undefined
// Safe approach: check first
if (user.address) {
console.log(user.address.city);
}
// Or use optional chaining (modern JavaScript)
console.log(user.address?.city); // undefined (no error)
Arrays have a guaranteed order, objects don’t. But it’s still possible to loop through their properties.
for...in loopWith the for...in loop you iterate over object keys (property names):
const product = {
name: 'Laptop',
price: 999,
brand: 'TechCorp'
};
for (const key in product) {
console.log(`${key}: ${product[key]}`);
}
// name: Laptop
// price: 999
// brand: TechCorp
Objects have helpful methods to work with their properties. The great thing about these is that they return arrays, which means you can use array methods and loop over the original objects.
const user = {
name: 'Alice',
age: 28,
city: 'Amsterdam'
};
// Object.keys() - returns array of property names
const keys = Object.keys(user);
console.log(keys); // ['name', 'age', 'city']
// Object.values() - returns array of property values
const values = Object.values(user);
console.log(values); // ['Alice', 28, 'Amsterdam']
// Object.entries() - returns array of [key, value] pairs
const entries = Object.entries(user);
console.log(entries);
// [['name', 'Alice'], ['age', 28], ['city', 'Amsterdam']]
Object.keys(), Object.values(), and Object.entries() are incredibly useful for converting objects into arrays, which gives you access to powerful array methods like map(), filter(), and reduce().
const scores = {
math: 85,
english: 92,
science: 78
};
// Calculate average using Object.values()
const allScores = Object.values(scores);
const average = allScores.reduce((sum, score) => sum + score, 0) / allScores.length;
console.log(`Average: ${average}`); // Average: 85
// List all subjects using Object.keys()
const subjects = Object.keys(scores);
for (const subject of subjects) {
console.log(subject.toUpperCase());
}
// MATH
// ENGLISH
// SCIENCE
// Create formatted list using Object.entries()
for (const [subject, score] of entries) {
console.log(`${subject}: ${score}%`);
}
// math: 85%
// english: 92%
// science: 78%
<aside> ⌨️
Create an object with 4-5 properties. Use Object.keys() to get all property names, then use a loop to print each one. Next, use Object.values() to calculate something (like a sum or count).
</aside>
Modern JavaScript allows you to create objects more concisely when variable names match property names:
// Traditional syntax
const name = 'Alice';
const age = 28;
const city = 'Amsterdam';
const user = {
name: name,
age: age,
city: city
};
// Property shorthand - much cleaner!
const user = {
name,
age,
city
};
// Same result: { name: 'Alice', age: 28, city: 'Amsterdam' }
This is especially useful when creating objects from function parameters or variables:
function createProduct(id, title, price) {
return {
id,
title,
price,
inStock: true // You can mix shorthand with regular properties
};
}
const laptop = createProduct(101, 'Laptop', 999);
console.log(laptop);
// { id: 101, title: 'Laptop', price: 999, inStock: true }
// Practical example: gathering form data
const username = 'alice123';
const email = '[email protected]';
const password = 'secret';
const formData = { username, email, password };
// Instead of: { username: username, email: email, password: password }
<aside> 💡
Property shorthand is one of those small features that makes your code significantly cleaner. Once you start using it, you'll never want to go back to the verbose (overly descriptive) syntax!
</aside>
The spread operator (...) allows you to copy properties from one object to another or merge multiple objects (later properties override earlier ones):
const user = {
name: 'Alice',
age: 28
};
// Create a copy
const userCopy = { ...user };
console.log(userCopy); // { name: 'Alice', age: 28 }
// Copy is independent from original
userCopy.age = 30;
console.log(user.age); // 28 (unchanged)
console.log(userCopy.age); // 30
// Add properties while copying
const extendedUser = {
...user,
email: '[email protected]',
isActive: true
};
console.log(extendedUser);
// { name: 'Alice', age: 28, email: '[email protected]', isActive: true }
// Override properties
const updatedUser = {
...user,
age: 29 // Overrides the age from user
};
console.log(updatedUser); // { name: 'Alice', age: 29 }
const defaults = {
theme: 'light',
language: 'en',
notifications: true
};
const userPreferences = {
theme: 'dark'
};
// Merge with user preferences taking priority
const settings = {
...defaults,
...userPreferences
};
console.log(settings);
// { theme: 'dark', language: 'en', notifications: true }
<aside> ⚠️
The spread operator creates a shallow copy. If your object contains nested objects or arrays, those nested structures are still referenced, not copied.
</aside>
const original = {
name: 'Alice',
address: {
city: 'Amsterdam'
}
};
const copy = { ...original };
// Modifying nested object affects both!
copy.address.city = 'Rotterdam';
console.log(original.address.city); // 'Rotterdam' (changed!)
// Top-level properties are independent
copy.name = 'Bob';
console.log(original.name); // 'Alice' (unchanged)
Destructuring allows you to extract properties from objects into variables with a clean, concise syntax.
<aside> 💡
Destructuring, spread operator, and property shorthand are modern JavaScript features you'll see everywhere in React, Node.js, and contemporary codebases. Learning them now will make reading other people's code much easier!
</aside>
const user = {
name: 'Alice',
age: 28,
city: 'Amsterdam'
};
// Traditional way
const name = user.name;
const age = user.age;
const city = user.city;
// Destructuring - extract multiple properties at once
const { name, age, city } = user;
console.log(name); // 'Alice'
console.log(age); // 28
console.log(city); // 'Amsterdam'
You can even customize the variable names you extract, provide default values, and destructure nested objects!
// Rename variables
const { name: userName, age: userAge } = user;
console.log(userName); // 'Alice'
// Default values for missing properties
const { name, country = 'Unknown' } = user;
console.log(country); // 'Unknown'
// Extract only what you need
const { name } = user; // Just get name, ignore rest
// Nested destructuring
const response = {
data: {
user: {
id: 123,
profile: {
name: 'Alice',
email: '[email protected]'
}
}
}
};
const { data: { user: { profile: { name, email } } } } = response;
console.log(name); // 'Alice'
console.log(email); // '[email protected]'
It is really helpful when using function parameters:
// Instead of accessing properties inside function
function greet(user) {
console.log(`Hello, ${user.name}! You are ${user.age} years old.`);
}
// Destructure in parameter (cleaner)
function greet({ name, age }) {
console.log(`Hello, ${name}! You are ${age} years old.`);
}
greet({ name: 'Alice', age: 28 });
// Hello, Alice! You are 28 years old.
// With default values
function createUser({ name, age, role = 'user' }) {
return { name, age, role };
}
console.log(createUser({ name: 'Bob', age: 25 }));
// { name: 'Bob', age: 25, role: 'user' }
<aside> ⌨️
Create an object with properties for a book (title, author, year, pages). Use destructuring to extract title and author into variables. Then create a function that takes a book object as parameter and uses destructuring in the parameter list.
</aside>
This is one of the most important concepts to understand about objects: objects are compared by reference, not by value.
// Primitives (numbers, strings, booleans) compare by value
const a = 5;
const b = 5;
console.log(a === b); // true (same value)
const str1 = 'hello';
const str2 = 'hello';
console.log(str1 === str2); // true (same value)
// Objects compare by reference (memory location)
const obj1 = { name: 'Alice' };
const obj2 = { name: 'Alice' };
console.log(obj1 === obj2); // false (different objects in memory!)
// Even if they look identical, they're different objects
const user1 = { name: 'Bob', age: 25 };
const user2 = { name: 'Bob', age: 25 };
console.log(user1 === user2); // false
Two objects are only equal if they reference the exact same object in memory:
const original = { name: 'Alice' };
const reference = original; // reference points to same object
console.log(original === reference); // true (same object in memory)
// Modifying through reference affects original
reference.name = 'Bob';
console.log(original.name); // 'Bob' (changed!)
This has implications for working with objects:
// Arrays are also objects, so same rules apply
const arr1 = [1, 2, 3];
const arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false (different arrays)
// Function parameters receive references
function changeAge(user) {
user.age = 30; // Modifies the original object!
}
const person = { name: 'Alice', age: 28 };
changeAge(person);
console.log(person.age); // 30 (original was modified)
// To avoid modifying original, create a copy
function changeAgeSafely(user) {
const copy = { ...user };
copy.age = 30;
return copy;
}
const person2 = { name: 'Bob', age: 25 };
const updated = changeAgeSafely(person2);
console.log(person2.age); // 25 (unchanged)
console.log(updated.age); // 30
<aside> ⚠️
When you assign an object to a new variable, you're copying the reference, not the object itself. Both variables point to the same object in memory, so changes through one variable affect the other.
</aside>
const settings = { theme: 'light' };
const userSettings = settings; // Copy reference, not object
userSettings.theme = 'dark';
console.log(settings.theme); // 'dark' (original changed!)
// To create an independent copy, use spread operator
const settingsCopy = { ...settings };
settingsCopy.theme = 'blue';
console.log(settings.theme); // 'dark' (unchanged)
console.log(settingsCopy.theme); // 'blue'
<aside> 💡
</aside>