Objects aren't just containers for data—they can also have behavior through methods. A method is a function stored as an object property, allowing objects to perform actions and manipulate their own data. The 'this' keyword is crucial for methods, providing a way to reference the object itself. Understanding methods and 'this' is essential for object-oriented programming in JavaScript. Let's master these powerful concepts!
What Are Object Methods?
A method is simply a function that is a property of an object. Methods allow objects to have both data (properties) and behavior (methods).
Real-world analogy:
- Car object: Properties (color, model) + Methods (start, stop, accelerate)
- User object: Properties (name, email) + Methods (login, logout, updateProfile)
- Calculator: Properties (display) + Methods (add, subtract, clear)
Creating Methods
// Object with methods
let person = {
name: "Alice",
age: 25,
// Method: function as property
greet: function() {
return "Hello, my name is " + this.name;
},
// Method shorthand (ES6) - cleaner!
introduce() {
return `I'm ${this.name} and I'm ${this.age} years old`;
},
// Method that uses other methods
sayHello() {
console.log(this.greet());
console.log(this.introduce());
}
};
// Call methods
console.log(person.greet()); // "Hello, my name is Alice"
console.log(person.introduce()); // "I'm Alice and I'm 25 years old"
person.sayHello(); // Calls both methodsMethod Shorthand (ES6)
Method shorthand is more concise
Traditional Syntax
// Traditional function syntax
let calculator = {
value: 0,
add: function(n) {
this.value += n;
return this.value;
},
subtract: function(n) {
this.value -= n;
return this.value;
},
reset: function() {
this.value = 0;
}
};
calculator.add(5);
console.log(calculator.value); // 5Method Shorthand (Cleaner)
// Method shorthand (ES6)
let calculator = {
value: 0,
add(n) {
this.value += n;
return this.value;
},
subtract(n) {
this.value -= n;
return this.value;
},
reset() {
this.value = 0;
}
};
calculator.add(5);
console.log(calculator.value); // 5The 'this' Keyword
The this keyword refers to the object that is executing the current method. It provides a way for methods to access other properties and methods of the same object.
Basic 'this' Usage
let person = {
firstName: "Alice",
lastName: "Smith",
age: 25,
// 'this' refers to the person object
getFullName() {
return this.firstName + " " + this.lastName;
},
greet() {
return `Hello, I'm ${this.getFullName()}`;
},
haveBirthday() {
this.age++; // Increment age property
return `Happy birthday! Now I'm ${this.age}`;
}
};
console.log(person.getFullName()); // "Alice Smith"
console.log(person.greet()); // "Hello, I'm Alice Smith"
console.log(person.haveBirthday()); // "Happy birthday! Now I'm 26"
console.log(person.age); // 26Why We Need 'this'
// Without 'this' - doesn't work!
let user = {
name: "Bob",
greet() {
// return "Hello, " + name; // Error! 'name' is not defined
return "Hello, " + this.name; // Correct! Access object's property
}
};
console.log(user.greet()); // "Hello, Bob"
// 'this' allows methods to access the object's data
let calculator = {
value: 0,
add(n) {
// Without 'this', we couldn't access 'value'
this.value += n;
return this; // Return object for chaining
},
multiply(n) {
this.value *= n;
return this;
},
getResult() {
return this.value;
}
};
// Method chaining
calculator.add(5).multiply(3);
console.log(calculator.getResult()); // 15'this' in Different Contexts
let person = {
name: "Alice",
// Regular function - 'this' works correctly
greet() {
console.log("Hello, " + this.name);
},
// Arrow function - 'this' doesn't refer to person!
greetArrow: () => {
console.log("Hello, " + this.name); // 'this' is NOT person
}
};
person.greet(); // "Hello, Alice" ✓
person.greetArrow(); // "Hello, undefined" ✗
// Why? Arrow functions don't have their own 'this'
// They inherit 'this' from parent scope (global in this case)
// Correct way with arrow functions (use regular functions for methods)
let person2 = {
name: "Bob",
greet() {
console.log("Hello, " + this.name);
}
};
person2.greet(); // "Hello, Bob" ✓Important: Don't use arrow functions for object methods if you need this to refer to the object. Arrow functions inherit this from their parent scope, not from the object.
'this' Behavior Patterns
Method Call Pattern
let person = {
name: "Alice",
greet() {
console.log("Hello, " + this.name);
}
};
// When called as a method, 'this' = object before the dot
person.greet(); // 'this' is person, outputs "Hello, Alice"
// But if you extract the method...
let greetFunction = person.greet;
greetFunction(); // 'this' is undefined (in strict mode) or global object
// Solution: Use bind() or call the method on the object
person.greet(); // Always works correctlyNested Functions Problem
let person = {
name: "Alice",
hobbies: ["reading", "coding", "gaming"],
// Problem: 'this' in nested function
showHobbies() {
this.hobbies.forEach(function(hobby) {
// 'this' here is NOT person!
console.log(this.name + " likes " + hobby); // undefined likes...
});
}
};
person.showHobbies(); // undefined likes reading, etc.
// Solution 1: Save 'this' in a variable
let person2 = {
name: "Bob",
hobbies: ["reading", "coding"],
showHobbies() {
let self = this; // Save reference to 'this'
this.hobbies.forEach(function(hobby) {
console.log(self.name + " likes " + hobby); // Works!
});
}
};
person2.showHobbies(); // Bob likes reading, Bob likes coding
// Solution 2: Use arrow function (inherits 'this')
let person3 = {
name: "Charlie",
hobbies: ["reading", "coding"],
showHobbies() {
// Arrow function inherits 'this' from showHobbies
this.hobbies.forEach(hobby => {
console.log(this.name + " likes " + hobby); // Works!
});
}
};
person3.showHobbies(); // Charlie likes reading, Charlie likes coding
// Solution 3: Pass 'this' as second argument to forEach
let person4 = {
name: "Diana",
hobbies: ["reading", "coding"],
showHobbies() {
this.hobbies.forEach(function(hobby) {
console.log(this.name + " likes " + hobby);
}, this); // Pass 'this' as context
}
};
person4.showHobbies(); // Diana likes reading, Diana likes codingPractical Method Patterns
Getters and Setters
let user = {
firstName: "Alice",
lastName: "Smith",
// Getter - access like a property
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
// Setter - set like a property
set fullName(name) {
let parts = name.split(" ");
this.firstName = parts[0];
this.lastName = parts[1];
}
};
// Use getter (no parentheses!)
console.log(user.fullName); // "Alice Smith"
// Use setter (no parentheses!)
user.fullName = "Bob Jones";
console.log(user.firstName); // "Bob"
console.log(user.lastName); // "Jones"
console.log(user.fullName); // "Bob Jones"
// Validation in setters
let product = {
_price: 0, // Convention: underscore for internal property
get price() {
return this._price;
},
set price(value) {
if (value < 0) {
console.log("Price cannot be negative");
return;
}
this._price = value;
}
};
product.price = 100;
console.log(product.price); // 100
product.price = -50; // "Price cannot be negative"
console.log(product.price); // 100 (unchanged)Method Chaining
let calculator = {
value: 0,
add(n) {
this.value += n;
return this; // Return the object for chaining
},
subtract(n) {
this.value -= n;
return this;
},
multiply(n) {
this.value *= n;
return this;
},
divide(n) {
if (n !== 0) {
this.value /= n;
}
return this;
},
reset() {
this.value = 0;
return this;
},
getResult() {
return this.value;
}
};
// Chain multiple operations
let result = calculator
.add(10)
.multiply(2)
.subtract(5)
.divide(3)
.getResult();
console.log(result); // 5
// Reset and chain again
calculator
.reset()
.add(5)
.multiply(4);
console.log(calculator.getResult()); // 20Factory Functions
// Factory function - creates objects with methods
function createUser(name, email) {
return {
name: name,
email: email,
isActive: true,
login() {
this.isActive = true;
return `${this.name} logged in`;
},
logout() {
this.isActive = false;
return `${this.name} logged out`;
},
updateEmail(newEmail) {
this.email = newEmail;
return `Email updated to ${this.email}`;
},
getInfo() {
return {
name: this.name,
email: this.email,
status: this.isActive ? "Active" : "Inactive"
};
}
};
}
// Create users
let user1 = createUser("Alice", "alice@test.com");
let user2 = createUser("Bob", "bob@test.com");
console.log(user1.login()); // "Alice logged in"
console.log(user1.updateEmail("alice.smith@test.com"));
console.log(user1.getInfo());
console.log(user2.logout()); // "Bob logged out"
console.log(user2.getInfo());Practical Examples
Example 1: Shopping Cart
let cart = {
items: [],
addItem(product, quantity = 1) {
// Check if item already exists
let existingItem = this.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
} else {
this.items.push({ product, quantity });
}
return this; // For chaining
},
removeItem(productId) {
this.items = this.items.filter(item => item.product.id !== productId);
return this;
},
updateQuantity(productId, quantity) {
let item = this.items.find(item => item.product.id === productId);
if (item) {
item.quantity = quantity;
}
return this;
},
getTotal() {
return this.items.reduce((total, item) => {
return total + (item.product.price * item.quantity);
}, 0);
},
getItemCount() {
return this.items.reduce((count, item) => count + item.quantity, 0);
},
clear() {
this.items = [];
return this;
},
getSummary() {
return {
itemCount: this.getItemCount(),
total: this.getTotal(),
items: this.items.map(item => ({
name: item.product.name,
quantity: item.quantity,
subtotal: item.product.price * item.quantity
}))
};
}
};
// Use the cart
let laptop = { id: 1, name: "Laptop", price: 1000 };
let mouse = { id: 2, name: "Mouse", price: 25 };
cart
.addItem(laptop, 1)
.addItem(mouse, 2);
console.log("Cart summary:", cart.getSummary());
console.log("Total:", cart.getTotal());
cart.updateQuantity(2, 3);
console.log("Updated total:", cart.getTotal());Example 2: Bank Account
function createBankAccount(accountNumber, initialBalance = 0) {
return {
accountNumber: accountNumber,
balance: initialBalance,
transactions: [],
deposit(amount) {
if (amount <= 0) {
return "Amount must be positive";
}
this.balance += amount;
this.transactions.push({
type: "deposit",
amount: amount,
date: new Date(),
balance: this.balance
});
return `Deposited $${amount}. New balance: $${this.balance}`;
},
withdraw(amount) {
if (amount <= 0) {
return "Amount must be positive";
}
if (amount > this.balance) {
return "Insufficient funds";
}
this.balance -= amount;
this.transactions.push({
type: "withdrawal",
amount: amount,
date: new Date(),
balance: this.balance
});
return `Withdrew $${amount}. New balance: $${this.balance}`;
},
getBalance() {
return `Current balance: $${this.balance}`;
},
getTransactionHistory() {
return this.transactions.map(t =>
`${t.type.toUpperCase()}: $${t.amount} (Balance: $${t.balance})`
);
},
transfer(amount, targetAccount) {
if (amount > this.balance) {
return "Insufficient funds for transfer";
}
this.withdraw(amount);
targetAccount.deposit(amount);
return `Transferred $${amount} to account ${targetAccount.accountNumber}`;
}
};
}
// Create accounts
let account1 = createBankAccount("ACC001", 1000);
let account2 = createBankAccount("ACC002", 500);
console.log(account1.deposit(500));
console.log(account1.withdraw(200));
console.log(account1.getBalance());
console.log(account1.transfer(300, account2));
console.log(account1.getBalance());
console.log(account2.getBalance());
console.log("\nTransaction history:");
account1.getTransactionHistory().forEach(t => console.log(t));Object Methods Practice
Experiment with object methods and 'this' keyword
console.log() to see your output in the console above.Key Takeaways
- Methods are functions stored as object properties
- Use method shorthand:
methodName() {} thisrefers to the object calling the methodthisallows methods to access object properties- Don't use arrow functions for methods that need
this - Arrow functions inherit
thisfrom parent scope - Return
thisfrom methods to enable chaining - Use getters/setters with
getandsetkeywords - Save
thisin a variable for nested functions - Methods enable objects to have both data and behavior
What's Next?
You now understand object methods and the 'this' keyword—how to add behavior to objects and access their properties from within methods! This is a crucial step toward object-oriented programming in JavaScript.
In the next lesson, we'll explore Object Destructuring—a powerful ES6 feature that lets you extract values from objects into variables with clean, concise syntax. This makes working with object data much more elegant!
💪 Practice Challenge:
Before moving on, try creating:
- A todo list object with methods to add, remove, and mark tasks complete
- A temperature converter with methods for Celsius/Fahrenheit conversion
- A user profile with getters/setters for validated properties
- A calculator that supports method chaining for multiple operations
- A game character with properties (health, level) and methods (attack, heal, levelUp)