What Are Objects?
Think of objects as containers that hold related information, like a driver's license that contains your name, address, birth date, and photo. In JavaScript, objects store collections of key-value pairs where each key is like a label and each value is the associated data.
Creating Objects
Just like there are many ways to build a house, there are several ways to create objects in JavaScript:
Object Literal Notation (Most Common)
// Empty object
const emptyObject = {};
// Object with properties
const person = {
firstName: 'John',
lastName: 'Smith',
age: 30,
isStudent: false
};
// Object with nested objects
const employee = {
id: 1001,
name: 'Sarah Johnson',
position: 'Software Engineer',
contact: {
email: 'sarah@company.com',
phone: '555-0123',
office: 'Building A, Room 302'
},
skills: ['JavaScript', 'React', 'Node.js']
};
Object Constructor
// Using Object constructor
const car = new Object();
car.make = 'Toyota';
car.model = 'Camry';
car.year = 2022;
// Using Object.create()
const prototypeObject = {
greet: function() {
return `Hello, I'm ${this.name}`;
}
};
const person = Object.create(prototypeObject);
person.name = 'Alice';
console.log(person.greet()); // "Hello, I'm Alice"
Accessing Object Properties
You can access object properties like reaching into different compartments of a toolbox:
Dot Notation vs Bracket Notation
const user = {
name: 'John Doe',
age: 25,
'favorite color': 'blue',
123: 'numeric key'
};
// Dot notation (preferred for simple property names)
console.log(user.name); // 'John Doe'
console.log(user.age); // 25
// Bracket notation (required for special cases)
console.log(user['favorite color']); // 'blue'
console.log(user[123]); // 'numeric key'
// Dynamic property access
const propertyName = 'age';
console.log(user[propertyName]); // 25
// Accessing nested properties
const company = {
name: 'Tech Corp',
address: {
street: '123 Silicon Valley',
city: 'San Francisco'
}
};
console.log(company.address.city); // 'San Francisco'
console.log(company['address']['street']); // '123 Silicon Valley'
Object Methods
Methods are functions stored as object properties - like actions that objects can perform:
const calculator = {
// Properties
brand: 'Casio',
model: 'FX-991',
// Methods
add: function(a, b) {
return a + b;
},
// ES6 method syntax
subtract(a, b) {
return a - b;
},
// Method using 'this' keyword
describe() {
return `${this.brand} ${this.model} Calculator`;
}
};
console.log(calculator.add(5, 3)); // 8
console.log(calculator.subtract(10, 4)); // 6
console.log(calculator.describe()); // "Casio FX-991 Calculator"
// Real-world example: User object
const user = {
firstName: 'Jane',
lastName: 'Smith',
birthYear: 1990,
getFullName() {
return `${this.firstName} ${this.lastName}`;
},
calculateAge() {
const currentYear = new Date().getFullYear();
return currentYear - this.birthYear;
},
canVote() {
return this.calculateAge() >= 18;
}
};
console.log(user.getFullName()); // "Jane Smith"
console.log(user.calculateAge()); // (current year - 1990)
console.log(user.canVote()); // true
Property Descriptors and Object Configuration
Properties have hidden attributes that control their behavior:
const product = {};
// Define a property with specific attributes
Object.defineProperty(product, 'id', {
value: 'PROD-001',
writable: false, // Cannot be changed
enumerable: true, // Shows up in for...in loops
configurable: false // Cannot be deleted or reconfigured
});
product.id = 'PROD-002'; // This won't work (in strict mode throws error)
console.log(product.id); // Still 'PROD-001'
// Define multiple properties at once
Object.defineProperties(product, {
name: {
value: 'Laptop',
writable: true,
enumerable: true
},
price: {
value: 999.99,
writable: true,
enumerable: true
}
});
// Get property descriptor
console.log(Object.getOwnPropertyDescriptor(product, 'id'));
// { value: 'PROD-001', writable: false, enumerable: true, configurable: false }
Iterating Over Objects
There are several ways to loop through object properties:
const student = {
name: 'Alex Johnson',
age: 20,
grade: 'A',
subjects: ['Math', 'Science', 'History']
};
// for...in loop
console.log('Using for...in:');
for (let key in student) {
console.log(`${key}: ${student[key]}`);
}
// Object.keys()
console.log('\nUsing Object.keys():');
Object.keys(student).forEach(key => {
console.log(`${key}: ${student[key]}`);
});
// Object.values()
console.log('\nUsing Object.values():');
Object.values(student).forEach(value => {
console.log(value);
});
// Object.entries()
console.log('\nUsing Object.entries():');
Object.entries(student).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
// Practical example: Converting object to query string
const params = {
search: 'javascript',
page: 2,
sort: 'relevance'
};
const queryString = Object.entries(params)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&');
console.log(queryString); // "search=javascript&page=2&sort=relevance"
Common Object Patterns
Object as a Dictionary/Map
// Word frequency counter
function countWords(text) {
const words = text.toLowerCase().split(/\s+/);
const frequency = {};
words.forEach(word => {
frequency[word] = (frequency[word] || 0) + 1;
});
return frequency;
}
const text = "hello world hello javascript world";
console.log(countWords(text));
// { hello: 2, world: 2, javascript: 1 }
// Phone book example
const phoneBook = {
'John Smith': '555-1234',
'Jane Doe': '555-5678',
'Bob Johnson': '555-9012'
};
function findPhoneNumber(name) {
return phoneBook[name] || 'Not found';
}
Object Factory Pattern
// Factory function to create user objects
function createUser(name, email, role = 'user') {
return {
name,
email,
role,
lastLogin: null,
login() {
this.lastLogin = new Date();
console.log(`${this.name} logged in at ${this.lastLogin}`);
},
logout() {
console.log(`${this.name} logged out`);
},
changeRole(newRole) {
this.role = newRole;
console.log(`${this.name}'s role changed to ${newRole}`);
}
};
}
const admin = createUser('Alice Admin', 'alice@example.com', 'admin');
const user = createUser('Bob User', 'bob@example.com');
admin.login();
user.changeRole('moderator');
Real-World Applications
Shopping Cart System
const shoppingCart = {
items: [],
addItem(product, quantity = 1) {
const existingItem = this.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ product, quantity });
}
},
removeItem(productId) {
this.items = this.items.filter(item => item.product.id !== productId);
},
updateQuantity(productId, quantity) {
const item = this.items.find(item => item.product.id === productId);
if (item) {
item.quantity = quantity;
}
},
getTotal() {
return this.items.reduce((total, item) => {
return total + (item.product.price * item.quantity);
}, 0);
},
checkout() {
const total = this.getTotal();
console.log(`Total: $${total.toFixed(2)}`);
// Process payment...
this.items = []; // Clear cart
}
};
// Usage
const product1 = { id: 1, name: 'Laptop', price: 999.99 };
const product2 = { id: 2, name: 'Mouse', price: 29.99 };
shoppingCart.addItem(product1);
shoppingCart.addItem(product2, 2);
console.log(shoppingCart.getTotal()); // 1059.97
Form Validation System
const formValidator = {
rules: {
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
message: 'Please enter a valid email address'
},
password: {
required: true,
minLength: 8,
message: 'Password must be at least 8 characters'
}
},
validate(formData) {
const errors = {};
Object.keys(this.rules).forEach(field => {
const rules = this.rules[field];
const value = formData[field];
if (rules.required && !value) {
errors[field] = `${field} is required`;
} else if (rules.pattern && !rules.pattern.test(value)) {
errors[field] = rules.message;
} else if (rules.minLength && value.length < rules.minLength) {
errors[field] = rules.message;
}
});
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
};
// Usage
const formData = {
email: 'user@example',
password: '12345'
};
const validation = formValidator.validate(formData);
console.log(validation);
// { isValid: false, errors: { email: 'Please enter a valid email address', password: 'Password must be at least 8 characters' } }
Best Practices
- Use meaningful property names: Choose descriptive names that clearly indicate purpose
- Prefer const for objects: Even though properties can change, the object reference should remain constant
- Use object destructuring: Extract multiple properties efficiently
- Avoid modifying objects you don't own: Don't add properties to built-in objects
- Use computed property names: When property names need to be dynamic
// Object destructuring
const user = { name: 'John', age: 30, email: 'john@example.com' };
const { name, email } = user;
// Computed property names
const propName = 'dynamicKey';
const obj = {
[propName]: 'value',
[`${propName}2`]: 'another value'
};
// Object spread operator
const defaults = { color: 'blue', size: 'medium' };
const userPrefs = { color: 'red' };
const finalSettings = { ...defaults, ...userPrefs };
// { color: 'red', size: 'medium' }
Practice Exercises
- Create a "Book" object with properties and methods to track reading progress
- Build a simple bank account object with deposit, withdraw, and balance check methods
- Create an object that represents a recipe with ingredients and cooking instructions
- Implement a student grade tracker that calculates averages and letter grades
Exercise Solutions (Try First!)
Click to see solutions
// 1. Book object
const book = {
title: 'JavaScript: The Good Parts',
author: 'Douglas Crockford',
totalPages: 176,
currentPage: 0,
readPages(pages) {
this.currentPage = Math.min(this.currentPage + pages, this.totalPages);
return this.getProgress();
},
getProgress() {
const percentage = (this.currentPage / this.totalPages) * 100;
return `${percentage.toFixed(1)}% complete`;
},
isFinished() {
return this.currentPage === this.totalPages;
}
};
// 2. Bank account object
const bankAccount = {
accountNumber: '123456789',
balance: 1000,
deposit(amount) {
if (amount > 0) {
this.balance += amount;
return `Deposited $${amount}. New balance: $${this.balance}`;
}
return 'Invalid deposit amount';
},
withdraw(amount) {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
return `Withdrew $${amount}. New balance: $${this.balance}`;
}
return 'Invalid withdrawal amount or insufficient funds';
},
checkBalance() {
return `Current balance: $${this.balance}`;
}
};
// 3. Recipe object
const recipe = {
name: 'Chocolate Chip Cookies',
servings: 24,
ingredients: {
'flour': '2 cups',
'sugar': '1 cup',
'butter': '1 cup',
'eggs': '2',
'chocolate chips': '2 cups'
},
instructions: [
'Preheat oven to 375°F',
'Mix dry ingredients',
'Cream butter and sugar',
'Add eggs',
'Combine wet and dry ingredients',
'Add chocolate chips',
'Bake for 10-12 minutes'
],
scaleRecipe(newServings) {
const ratio = newServings / this.servings;
const scaledIngredients = {};
Object.entries(this.ingredients).forEach(([ingredient, amount]) => {
const [quantity, unit] = amount.split(' ');
const scaledQuantity = parseFloat(quantity) * ratio;
scaledIngredients[ingredient] = `${scaledQuantity} ${unit || ''}`.trim();
});
return scaledIngredients;
}
};
// 4. Student grade tracker
const studentGrades = {
name: 'Alice Johnson',
grades: [],
addGrade(subject, score) {
this.grades.push({ subject, score });
},
getAverage() {
if (this.grades.length === 0) return 0;
const sum = this.grades.reduce((total, grade) => total + grade.score, 0);
return sum / this.grades.length;
},
getLetterGrade() {
const average = this.getAverage();
if (average >= 90) return 'A';
if (average >= 80) return 'B';
if (average >= 70) return 'C';
if (average >= 60) return 'D';
return 'F';
},
getTranscript() {
return {
student: this.name,
grades: this.grades,
average: this.getAverage().toFixed(2),
letterGrade: this.getLetterGrade()
};
}
};
Summary
Objects are fundamental to JavaScript and provide:
- A way to group related data and functionality
- Flexible property access with dot and bracket notation
- Dynamic property addition and deletion
- Methods for object-oriented programming
- Multiple ways to iterate over properties
Next up: We'll explore array methods like map, filter, and reduce that transform how we work with collections of data!