ποΈ Object Literals and Properties
Arrays organize data by position. But most real-world things β a user, a product, a blog post β are better described by named attributes: a user has a name, an email, a role. The object is JavaScript's tool for grouping related values under descriptive keys, and it is the single most important data structure in the language.
Week 2 · Day 1 (Monday: Arrays and Objects) · Lecture 2
π― Learning Objectives
By the end of this lesson, you will be able to:
- Create objects with literal notation and nest objects and arrays inside them
- Access properties with both dot and bracket notation, and know when each is required
- Add, modify, and delete properties, and check whether a property exists
- Write methods and use the
thiskeyword to reference the owning object - Iterate over an object with
Object.keys,values, andentries - Apply modern patterns: destructuring, computed keys, and the spread operator
Estimated Time: 65 minutes
Practice: Build a reading-tracker, a bank account, and a student grade tracker as objects.
In This Lesson
What Are Objects?
Think of an object as a driver's license. A single card groups together your name, address, birth date, and photo β each fact clearly labeled. In JavaScript, an object stores a collection of keyβvalue pairs: each key is the label, and each value is the associated data. Values can be anything β strings, numbers, arrays, functions, or even other objects.
Notice how address is itself an object nested inside person. This ability to nest β objects inside objects, arrays inside objects, objects inside arrays β is what lets a handful of simple rules describe almost any real-world data.
Creating Objects
As with houses, there are several ways to build an object. The object literal β a pair of curly braces β is by far the most common and the one you'll use daily.
Object literal notation (the go-to)
// Empty object, ready to fill
const emptyObject = {};
// Object with properties
const person = {
firstName: 'John',
lastName: 'Smith',
age: 30,
isStudent: false
};
// Objects can nest other objects and arrays
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']
};
Other ways (good to recognize)
// Object constructor β verbose; the literal above is preferred
const car = new Object();
car.make = 'Toyota';
car.model = 'Camry';
car.year = 2022;
// Object.create() β makes a new object with a given prototype (advanced)
const prototypeObject = {
greet() {
return `Hello, I'm ${this.name}`;
}
};
const alice = Object.create(prototypeObject);
alice.name = 'Alice';
console.log(alice.greet()); // "Hello, I'm Alice"
π‘ Object vs array β which do I reach for?
Use an array when the data is an ordered list you'll loop over (todo items, search results). Use an object when the data is a set of named attributes you'll look up by key (a user's name and email). In practice you constantly nest one inside the other.
Dot vs Bracket Access
Reading a property is like reaching into a labeled compartment of a toolbox. JavaScript gives you two syntaxes β and knowing exactly when bracket notation is required saves real debugging time.
const user = {
name: 'John Doe',
age: 25,
'favorite color': 'blue', // key with a space
123: 'numeric key'
};
// Dot notation β clean and preferred for normal names
console.log(user.name); // 'John Doe'
console.log(user.age); // 25
// Bracket notation β REQUIRED when the key has spaces or is numeric
console.log(user['favorite color']); // 'blue'
console.log(user[123]); // 'numeric key'
// Bracket notation is also REQUIRED for dynamic (variable) keys
const propertyName = 'age';
console.log(user[propertyName]); // 25 β reads the "age" property
console.log(user.propertyName); // undefined β looks for a literal "propertyName" key!
π The rule of thumb
Use dot notation by default. Switch to bracket notation in exactly two situations: the key isn't a valid identifier (it has spaces, dashes, or starts with a digit), or the key is stored in a variable and decided at runtime.
Reaching into nested objects
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'
// Optional chaining (?.) safely handles a missing branch β no crash
console.log(company.address?.zip); // undefined (no error)
console.log(company.ceo?.name); // undefined (ceo doesn't exist)
β
Guard deep access with ?.
Reading company.ceo.name when ceo is missing throws a TypeError. Optional chaining β company.ceo?.name β short-circuits to undefined instead, which is invaluable when handling data from an API you don't fully control.
Adding, Modifying & Deleting Properties
Objects are mutable: you can change them freely after creation, even when declared with const. (The const locks the binding β the object it points to β not the object's contents.)
const car = { make: 'Honda', model: 'Civic' };
// Adding new properties
car.year = 2023;
car['color'] = 'red';
console.log(car); // { make: 'Honda', model: 'Civic', year: 2023, color: 'red' }
// Modifying existing properties
car.color = 'blue';
car['year'] = 2024;
// Deleting a property
delete car.color;
console.log(car); // { make: 'Honda', model: 'Civic', year: 2024 }
// Checking whether a property exists
console.log('make' in car); // true
console.log('color' in car); // false
console.log(Object.hasOwn(car, 'model')); // true (modern; replaces hasOwnProperty)
π‘ Object.hasOwn() over hasOwnProperty()
Object.hasOwn(obj, key) (ES2022) is the modern, safer way to check for an own property. It works even on objects created with Object.create(null), where the old obj.hasOwnProperty(key) would fail.
Methods & the this Keyword
A method is simply a function stored as an object property β an action the object can perform. Inside a method, the keyword this refers to the object the method was called on, letting the method read and update its own object's data.
const calculator = {
brand: 'Casio',
model: 'FX-991',
// Full function-expression syntax
add: function (a, b) {
return a + b;
},
// ES6 shorthand method syntax (preferred)
subtract(a, b) {
return a - b;
},
// A method using `this` to read its own properties
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"
A richer example: a user object that computes
const user = {
firstName: 'Jane',
lastName: 'Smith',
birthYear: 1990,
getFullName() {
return `${this.firstName} ${this.lastName}`;
},
calculateAge() {
const currentYear = new Date().getFullYear();
return currentYear - this.birthYear;
},
// Methods can call other methods via `this`
canVote() {
return this.calculateAge() >= 18;
}
};
console.log(user.getFullName()); // "Jane Smith"
console.log(user.calculateAge()); // e.g. 36 in 2026
console.log(user.canVote()); // true
β οΈ Don't use an arrow function as a method that needs this
Arrow functions don't get their own this β they inherit it from the surrounding scope. If you wrote describe: () => `${this.brand}...`, this would not be the object and this.brand would be undefined. Use the shorthand method syntax (describe() { ... }) for object methods.
Iterating Over Objects
Sometimes you need to walk through every property β to display it, transform it, or count it. There are four main tools, and the modern Object.entries() is usually the most convenient.
const student = {
name: 'Alex Johnson',
age: 20,
grade: 'A'
};
// 1. for...in β loops over the keys
for (const key in student) {
console.log(`${key}: ${student[key]}`);
}
// 2. Object.keys() β an array of the keys
console.log(Object.keys(student)); // ['name', 'age', 'grade']
// 3. Object.values() β an array of the values
console.log(Object.values(student)); // ['Alex Johnson', 20, 'A']
// 4. Object.entries() β an array of [key, value] pairs (great with destructuring)
Object.entries(student).forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
Practical: object β URL 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"
Practical: an object as a lookup dictionary
// Count how often each word appears β object as a frequency map
function countWords(text) {
const frequency = {};
for (const word of text.toLowerCase().split(/\s+/)) {
frequency[word] = (frequency[word] || 0) + 1;
}
return frequency;
}
console.log(countWords('hello world hello javascript world'));
// { hello: 2, world: 2, javascript: 1 }
Destructuring & Spread
Two ES6 features make working with objects dramatically cleaner. You'll see them in nearly every modern codebase, especially React.
Destructuring β pull properties into variables
const user = { name: 'John', age: 30, email: 'john@example.com' };
// Extract several properties in one line
const { name, email } = user;
console.log(name, email); // "John" "john@example.com"
// Rename while destructuring, and set defaults for missing keys
const { name: userName, role = 'guest' } = user;
console.log(userName, role); // "John" "guest"
Spread β copy and merge objects
// Merge defaults with overrides β later keys win
const defaults = { color: 'blue', size: 'medium' };
const userPrefs = { color: 'red' };
const finalSettings = { ...defaults, ...userPrefs };
console.log(finalSettings); // { color: 'red', size: 'medium' }
// Make a shallow copy without touching the original
const copy = { ...user, age: 31 }; // copy has age 31; user is untouched
Computed property names
// Use a variable's value as the KEY, with [ ] around it
const propName = 'dynamicKey';
const obj = {
[propName]: 'value',
[`${propName}2`]: 'another value'
};
console.log(obj); // { dynamicKey: 'value', dynamicKey2: 'another value' }
β οΈ Spread copies are shallow
{ ...user } copies top-level properties, but nested objects are still shared. Mutating copy.address.city would also change user.address.city. For a fully independent copy of plain data, use structuredClone(user).
Practice & Quiz
ποΈ Exercise 1: A reading tracker
Goal: Build a book object with totalPages, a currentPage, a readPages(n) method, and a getProgress() method that returns a percentage string.
π‘ Hint
Use this.currentPage inside the methods. Cap progress with Math.min(this.currentPage + n, this.totalPages) so you never read past the end.
β Solution
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;
}
};
console.log(book.readPages(88)); // "50.0% complete"
ποΈ Exercise 2: A bank account
Goal: Create a bankAccount object with a balance, plus deposit(amount) and withdraw(amount) methods that validate the input and update the balance.
β Solution
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 amount or insufficient funds';
},
checkBalance() {
return `Current balance: $${this.balance}`;
}
};
console.log(bankAccount.deposit(500)); // "Deposited $500. New balance: $1500"
console.log(bankAccount.withdraw(200)); // "Withdrew $200. New balance: $1300"
ποΈ Exercise 3: A student grade tracker
Goal: Build a studentGrades object that stores grades in an array and can compute an average and a letter grade.
β Solution
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, g) => total + g.score, 0);
return sum / this.grades.length;
},
getLetterGrade() {
const avg = this.getAverage();
if (avg >= 90) return 'A';
if (avg >= 80) return 'B';
if (avg >= 70) return 'C';
if (avg >= 60) return 'D';
return 'F';
}
};
studentGrades.addGrade('Math', 92);
studentGrades.addGrade('Science', 85);
console.log(studentGrades.getAverage()); // 88.5
console.log(studentGrades.getLetterGrade()); // "B"
π― Quick Quiz
Question 1: Given const k = 'age'; and an object user, how do you read the property whose name is stored in k?
Question 2: Inside an object method, what does this refer to?
Question 3: Which returns an array of [key, value] pairs?
Best Practices & Pitfalls
β Do
- Declare objects with
constβ you can still mutate properties; the reference stays fixed - Choose clear, descriptive property names (
createdAt, notd) - Use destructuring to pull out the few properties you need
- Use the shorthand method syntax and guard deep reads with optional chaining
?.
β Don't
- Use an arrow function as a method that relies on
this - Add properties to built-in objects you don't own (like
Object.prototype) - Assume
{ ...obj }deep-copies β nested objects are still shared - Reach into
a.b.cwithout checking thatbexists first
β οΈ Property order & integer-like keys
Objects preserve insertion order for string keys β but keys that look like non-negative integers ("1", "2") are always iterated first, in numeric order. If you need guaranteed ordering or non-string keys, reach for a Map instead of a plain object.
Summary
π Key Takeaways
- Objects group related data as keyβvalue pairs β create them with a literal
{} - Use dot notation by default; bracket notation for special or dynamic keys
- Objects are mutable even under
constβ add, change, anddeletefreely - A method is a function property;
thispoints to the owning object - Iterate with
Object.keys/values/entries; simplify with destructuring and spread
π Additional Resources
π What's Next?
You can now store data in arrays and objects. Next you'll learn the elegant, functional way to transform that data β the trio every JavaScript developer uses constantly: Array Methods (map, filter, reduce).
π Well done!
Objects are the foundation of everything from JSON to React state. You now know how to build, read, and reshape them with confidence.