Objects
An object is a collection of key-value pairs. Objects are the primary way to group related data and behavior in JavaScript.
Object literals
The most common way to create an object:
const person = {
name: "Ada",
age: 36,
city: "London",
};
console.log(person);
Result:
{ name: 'Ada', age: 36, city: 'London' }
Keys (also called properties) are strings. Values can be anything -- strings, numbers, booleans, arrays, other objects, functions.
Accessing properties
Dot notation
const user = { name: "Ada", age: 36 };
console.log(user.name);
console.log(user.age);
Result:
Ada
36
Bracket notation
const user = { name: "Ada", age: 36 };
console.log(user["name"]);
console.log(user["age"]);
Result:
Ada
36
Bracket notation is required when:
- The key is stored in a variable
- The key contains spaces or special characters
const key = "name";
const user = { name: "Ada", "full name": "Ada Lovelace" };
console.log(user[key]);
console.log(user["full name"]);
Result:
Ada
Ada Lovelace
Accessing non-existent properties
const user = { name: "Ada" };
console.log(user.email);
Result:
undefined
No error -- just undefined.
Optional chaining (?.)
Safely access deeply nested properties without errors:
const user = {
name: "Ada",
address: {
city: "London",
},
};
console.log(user.address.city);
console.log(user.address?.zip);
console.log(user.company?.name);
// Without ?. this would throw: user.company.name
Result:
London
undefined
undefined
Modifying properties
const car = {
brand: "Toyota",
year: 2020,
};
// Update
car.year = 2024;
console.log(car);
// Add
car.color = "blue";
console.log(car);
// Delete
delete car.color;
console.log(car);
Result:
{ brand: 'Toyota', year: 2024 }
{ brand: 'Toyota', year: 2024, color: 'blue' }
{ brand: 'Toyota', year: 2024 }
Like arrays, const prevents reassigning the variable, not modifying the object.
Checking if a property exists
const user = { name: "Ada", age: 36 };
// 'in' operator
console.log("name" in user);
console.log("email" in user);
// hasOwnProperty
console.log(user.hasOwnProperty("age"));
// Compare to undefined (less reliable)
console.log(user.name !== undefined);
Result:
true
false
true
true
Methods
A method is a function stored as an object property:
const calculator = {
add(a, b) {
return a + b;
},
subtract(a, b) {
return a - b;
},
};
console.log(calculator.add(5, 3));
console.log(calculator.subtract(10, 4));
Result:
8
6
The shorthand add(a, b) {} is equivalent to add: function(a, b) {}.
The this keyword
Inside a method, this refers to the object the method belongs to:
const person = {
name: "Ada",
greet() {
return `Hello, I'm ${this.name}`;
},
};
console.log(person.greet());
Result:
Hello, I'm Ada
this pitfall with arrow functions
Arrow functions do not have their own this -- they inherit it from the surrounding scope:
const person = {
name: "Ada",
// Arrow function -- 'this' is NOT the object
greetArrow: () => {
return `Hello, I'm ${this.name}`;
},
// Regular function -- 'this' IS the object
greetRegular() {
return `Hello, I'm ${this.name}`;
},
};
console.log(person.greetRegular());
console.log(person.greetArrow());
Result:
Hello, I'm Ada
Hello, I'm undefined
Rule: Use regular functions (shorthand syntax) for object methods. Use arrow functions for callbacks and standalone functions.
Computed property names
Use expressions as keys with bracket syntax:
const field = "email";
const user = {
name: "Ada",
[field]: "ada@example.com",
[`${field}Verified`]: true,
};
console.log(user);
Result:
{ name: 'Ada', email: 'ada@example.com', emailVerified: true }
Shorthand properties
When a variable name matches the property name:
const name = "Ada";
const age = 36;
// Without shorthand
const user1 = { name: name, age: age };
// With shorthand
const user2 = { name, age };
console.log(user1);
console.log(user2);
Result:
{ name: 'Ada', age: 36 }
{ name: 'Ada', age: 36 }
Iterating over objects
Object.keys() -- get all keys
const user = { name: "Ada", age: 36, city: "London" };
console.log(Object.keys(user));
Result:
[ 'name', 'age', 'city' ]
Object.values() -- get all values
const user = { name: "Ada", age: 36, city: "London" };
console.log(Object.values(user));
Result:
[ 'Ada', 36, 'London' ]
Object.entries() -- get key-value pairs
const user = { name: "Ada", age: 36, city: "London" };
console.log(Object.entries(user));
Result:
[ [ 'name', 'Ada' ], [ 'age', 36 ], [ 'city', 'London' ] ]
Looping with for...in
const scores = { math: 95, english: 87, science: 92 };
for (const subject in scores) {
console.log(`${subject}: ${scores[subject]}`);
}
Result:
math: 95
english: 87
science: 92
Looping with Object.entries and for...of
const scores = { math: 95, english: 87, science: 92 };
for (const [subject, score] of Object.entries(scores)) {
console.log(`${subject}: ${score}`);
}
Result:
math: 95
english: 87
science: 92
This pattern (destructuring in the loop) is clean and widely used.
Destructuring
Extract properties into separate variables:
const user = { name: "Ada", age: 36, city: "London" };
const { name, age, city } = user;
console.log(name);
console.log(age);
console.log(city);
Result:
Ada
36
London
Renaming during destructuring
const user = { name: "Ada", age: 36 };
const { name: userName, age: userAge } = user;
console.log(userName);
console.log(userAge);
Result:
Ada
36
Default values
const user = { name: "Ada" };
const { name, age = 0, role = "user" } = user;
console.log(name);
console.log(age);
console.log(role);
Result:
Ada
0
user
Nested destructuring
const company = {
name: "TechCorp",
address: {
city: "London",
country: "UK",
},
};
const {
name,
address: { city, country },
} = company;
console.log(name);
console.log(city);
console.log(country);
Result:
TechCorp
London
UK
Destructuring in function parameters
Very common pattern:
function createUser({ name, age, role = "user" }) {
return `${name} (${age}) -- ${role}`;
}
const result = createUser({ name: "Ada", age: 36, role: "admin" });
console.log(result);
const result2 = createUser({ name: "Bob", age: 25 });
console.log(result2);
Result:
Ada (36) -- admin
Bob (25) -- user
Spread operator
Copy and merge objects:
const defaults = { theme: "dark", language: "en", fontSize: 14 };
const userPrefs = { theme: "light", fontSize: 16 };
// Merge -- later properties overwrite earlier ones
const settings = { ...defaults, ...userPrefs };
console.log(settings);
// Copy
const copy = { ...defaults };
copy.theme = "blue";
console.log(defaults.theme); // original unchanged
console.log(copy.theme);
Result:
{ theme: 'light', language: 'en', fontSize: 16 }
dark
blue
Rest with objects
Collect remaining properties:
const user = { name: "Ada", age: 36, city: "London", role: "admin" };
const { name, ...rest } = user;
console.log(name);
console.log(rest);
Result:
Ada
{ age: 36, city: 'London', role: 'admin' }
This is useful for removing properties or separating known fields from the rest.
Comparing objects
Objects are compared by reference, not by value:
const a = { name: "Ada" };
const b = { name: "Ada" };
const c = a;
console.log(a === b); // different objects, same content
console.log(a === c); // same reference
Result:
false
true
To compare contents, you need to check each property or use JSON.stringify (for simple cases):
const a = { name: "Ada", age: 36 };
const b = { name: "Ada", age: 36 };
console.log(JSON.stringify(a) === JSON.stringify(b));
Result:
true
Note: JSON.stringify comparison is fragile -- it depends on property order and does not handle all types. For robust
deep comparison, use a library or write a recursive function.
JSON
JSON (JavaScript Object Notation) is a text format for data exchange. It looks like JavaScript objects but with stricter rules:
- Keys must be double-quoted strings
- No trailing commas
- No functions,
undefined, orSymbol
JSON.stringify -- object to JSON string
const user = { name: "Ada", age: 36, active: true };
const json = JSON.stringify(user);
console.log(json);
console.log(typeof json);
Result:
{"name":"Ada","age":36,"active":true}
string
Pretty-print with indentation:
const user = { name: "Ada", age: 36 };
console.log(JSON.stringify(user, null, 2));
Result:
{
"name": "Ada",
"age": 36
}
JSON.parse -- JSON string to object
const json = '{"name":"Ada","age":36}';
const user = JSON.parse(json);
console.log(user);
console.log(user.name);
Result:
{ name: 'Ada', age: 36 }
Ada
Always wrap JSON.parse in a try/catch -- invalid JSON throws an error:
try {
const data = JSON.parse("not valid json");
} catch (error) {
console.log("Parse error:", error.message);
}
Result:
Parse error: Unexpected token 'o', "not valid json" is not valid JSON
For a deep dive into JSON handling, see the JSON Parsing Guide.
Nested objects
Objects inside objects:
const school = {
name: "Tech Academy",
address: {
street: "123 Main St",
city: "London",
},
students: [
{ name: "Ada", grade: "A" },
{ name: "Bob", grade: "B" },
],
};
console.log(school.address.city);
console.log(school.students[0].name);
console.log(school.students.length);
Result:
London
Ada
2
Shallow vs deep copies
The spread operator makes a shallow copy -- nested objects are still shared:
const original = {
name: "Ada",
scores: [90, 85, 92],
};
const shallow = { ...original };
shallow.name = "Bob";
shallow.scores.push(100); // modifies the shared array!
console.log(original.name); // unchanged
console.log(original.scores); // changed!
Result:
Ada
[ 90, 85, 92, 100 ]
For a deep copy, use structuredClone:
const original = {
name: "Ada",
scores: [90, 85, 92],
};
const deep = structuredClone(original);
deep.scores.push(100);
console.log(original.scores);
console.log(deep.scores);
Result:
[ 90, 85, 92 ]
[ 90, 85, 92, 100 ]
Object.freeze and Object.assign
Object.freeze -- prevent modifications
const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000,
});
config.timeout = 9999; // silently ignored (throws in strict mode)
console.log(config.timeout);
Result:
5000
Note: freeze is shallow. Nested objects can still be modified.
Object.assign -- copy properties
const target = { a: 1 };
const source = { b: 2, c: 3 };
Object.assign(target, source);
console.log(target);
Result:
{ a: 1, b: 2, c: 3 }
In modern code, the spread operator {...target, ...source} is preferred over Object.assign.
Summary
- Objects are key-value pairs created with
{}. - Access properties with dot notation (
obj.key) or brackets (obj["key"]). - Optional chaining
?.prevents errors on nested access. - Methods are functions on objects; use regular functions (not arrows) for methods that need
this. Object.keys(),.values(),.entries()iterate over objects.- Destructuring extracts properties; spread
...copies and merges objects. - JSON is the standard text format for data exchange -- use
JSON.stringifyandJSON.parse. - Shallow copies share nested references; use
structuredClonefor deep copies.
Next up: HTML & CSS Essentials -- the building blocks of web pages, just enough to start using JavaScript in the browser.