Objects
Key/value maps and prototypes
JavaScript Objects
Objects are collections of key-value pairs (properties). They're fundamental to JavaScript—almost everything is an object!
Creating Objects
// Object literal (most common)
const person = {
name: "Alice",
age: 30,
isEmployed: true
};
// Using new Object()
const car = new Object();
car.brand = "Toyota";
car.year = 2024;
// Using Object.create()
const prototype = { greet() { return "Hello!"; } };
const obj = Object.create(prototype);
Accessing Properties
const user = {
name: "Bob",
age: 25,
"favorite color": "blue" // Keys with spaces need quotes
};
// Dot notation (preferred)
console.log(user.name); // "Bob"
// Bracket notation (for dynamic keys or special characters)
console.log(user["age"]); // 25
console.log(user["favorite color"]); // "blue"
// Dynamic access
const key = "name";
console.log(user[key]); // "Bob"
Adding, Updating, Deleting Properties
const user = { name: "Alice" };
// Add property
user.age = 30;
user["email"] = "alice@example.com";
// Update property
user.age = 31;
// Delete property
delete user.email;
console.log(user); // { name: "Alice", age: 31 }
Methods (Functions in Objects)
const person = {
name: "Alice",
age: 30,
// Method (traditional)
greet: function() {
return `Hello, I'm ${this.name}`;
},
// Method (shorthand - ES6)
introduce() {
return `I'm ${this.name}, ${this.age} years old`;
},
// Arrow functions don't have their own 'this'
// Don't use arrow functions for methods!
badMethod: () => {
return `Hello, I'm ${this.name}`; // 'this' is undefined!
}
};
console.log(person.greet()); // "Hello, I'm Alice"
console.log(person.introduce()); // "I'm Alice, 30 years old"
The 'this' Keyword
this refers to the object the method is called on:
const user = {
name: "Bob",
greet() {
console.log(`Hi, I'm ${this.name}`);
}
};
user.greet(); // "Hi, I'm Bob" - 'this' is 'user'
// 'this' can be lost when function is detached
const greetFunc = user.greet;
greetFunc(); // Error or undefined - 'this' is not 'user'
// Solution: bind, call, or apply
const boundGreet = user.greet.bind(user);
boundGreet(); // "Hi, I'm Bob"
Object Destructuring
const user = {
name: "Alice",
age: 30,
email: "alice@example.com"
};
// Extract properties into variables
const { name, age } = user;
console.log(name); // "Alice"
console.log(age); // 30
// Rename while destructuring
const { name: userName, age: userAge } = user;
console.log(userName); // "Alice"
// Default values
const { phone = "N/A" } = user;
console.log(phone); // "N/A"
// Nested destructuring
const person = {
name: "Bob",
address: { city: "NYC", zip: "10001" }
};
const { address: { city } } = person;
console.log(city); // "NYC"
Object Spread & Rest
// Spread: copy properties
const user = { name: "Alice", age: 30 };
const updatedUser = { ...user, age: 31 };
// { name: "Alice", age: 31 }
// Merge objects
const defaults = { theme: "light", lang: "en" };
const userPrefs = { theme: "dark" };
const prefs = { ...defaults, ...userPrefs };
// { theme: "dark", lang: "en" }
// Rest: collect remaining properties
const { name, ...rest } = { name: "Bob", age: 25, city: "NYC" };
console.log(name); // "Bob"
console.log(rest); // { age: 25, city: "NYC" }
Useful Object Methods
const user = { name: "Alice", age: 30, city: "NYC" };
// Get keys, values, or entries
Object.keys(user); // ["name", "age", "city"]
Object.values(user); // ["Alice", 30, "NYC"]
Object.entries(user); // [["name", "Alice"], ["age", 30], ["city", "NYC"]]
// Check if property exists
"name" in user; // true
user.hasOwnProperty("name"); // true
// Freeze (make immutable)
const frozen = Object.freeze({ x: 1 });
frozen.x = 2; // Ignored in strict mode, error in strict mode
// Assign (merge)
const target = { a: 1 };
const source = { b: 2 };
Object.assign(target, source); // { a: 1, b: 2 }
Computed Property Names
const key = "favoriteColor";
const value = "blue";
// Old way
const obj1 = {};
obj1[key] = value;
// ES6 way
const obj2 = {
[key]: value,
[`${key}_secondary`]: "green"
};
// { favoriteColor: "blue", favoriteColor_secondary: "green" }
Classes (Modern OOP)
class Person {
// Constructor
constructor(name, age) {
this.name = name;
this.age = age;
}
// Method
greet() {
return `Hello, I'm ${this.name}`;
}
// Getter
get birthYear() {
return new Date().getFullYear() - this.age;
}
// Static method
static species() {
return "Homo sapiens";
}
}
const alice = new Person("Alice", 30);
console.log(alice.greet()); // "Hello, I'm Alice"
console.log(alice.birthYear); // 1996 (or current year - 30)
console.log(Person.species()); // "Homo sapiens"
// Inheritance
class Employee extends Person {
constructor(name, age, role) {
super(name, age); // Call parent constructor
this.role = role;
}
introduce() {
return `${this.greet()}, I'm a ${this.role}`;
}
}
const bob = new Employee("Bob", 25, "Developer");
console.log(bob.introduce()); // "Hello, I'm Bob, I'm a Developer"
⚠️ Common Mistakes
- • Using arrow functions for methods (loses
thiscontext) - • Forgetting that objects are passed by reference
- • Modifying objects you don't own (use spread to copy first)
- • Not checking if a property exists before accessing nested values
✓ Best Practices
- • Use object destructuring for cleaner code
- • Use spread operator to copy objects instead of mutating
- • Use optional chaining (
?.) for safe property access - • Prefer classes over constructor functions for OOP
- • Use
Object.freeze()for immutable objects