Docs
10.2-Object-Methods
7.2 Object Methods
Table of Contents
- ā¢Introduction
- ā¢Defining Methods
- ā¢Method Shorthand Syntax
- ā¢The
thisKeyword in Methods - ā¢Method Invocation Patterns
- ā¢Arrow Functions as Methods
- ā¢Chaining Methods
- ā¢Computed Method Names
- ā¢Best Practices
- ā¢Common Pitfalls
Introduction
Object methods are functions stored as object properties. They allow objects to have behavior, not just data.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Object Anatomy ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā const user = { ā
ā name: "Alice", ā Property (data) ā
ā age: 30, ā Property (data) ā
ā greet() { ... } ā Method (behavior)ā
ā }; ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Why Methods Matter:
- ā¢Encapsulate behavior with related data
- ā¢Enable object-oriented programming patterns
- ā¢Provide clean APIs for interacting with objects
- ā¢Allow objects to manipulate their own state
Defining Methods
Traditional Function Expression
const calculator = {
value: 0,
add: function (num) {
this.value += num;
return this;
},
subtract: function (num) {
this.value -= num;
return this;
},
};
ES6 Method Shorthand (Preferred)
const calculator = {
value: 0,
add(num) {
this.value += num;
return this;
},
subtract(num) {
this.value -= num;
return this;
},
};
Method Shorthand Syntax
ES6 introduced a cleaner way to define methods:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Method Definition Comparison ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāā¬āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Traditional (ES5) ā Shorthand (ES6+) ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā method: function() { ā method() { ā
ā // code ā // code ā
ā } ā } ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāā¼āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā getName: function() { ā getName() { ā
ā return this.name; ā return this.name; ā
ā } ā } ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāā“āāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Benefits of Shorthand
| Feature | Traditional | Shorthand |
|---|---|---|
| Syntax | Verbose | Concise |
| Readability | Good | Better |
super access | No | Yes |
| Use as constructor | Yes | No |
[[HomeObject]] | No | Yes |
The this Keyword in Methods
The this keyword refers to the object that is executing the current method.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā How `this` Works in Methods ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā const user = { ā
ā name: "Bob", ā
ā greet() { ā
ā console.log(`Hello, ${this.name}`); ā
ā } ā ā
ā }; ā ā
ā ā ā
ā user.greet();āāāāā `this` refers to `user` ā
ā ā
ā Output: "Hello, Bob" ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
this is Determined at Call Time
const person = {
name: 'Alice',
greet() {
console.log(`Hi, I'm ${this.name}`);
},
};
person.greet(); // Hi, I'm Alice
// Extract the method
const greetFn = person.greet;
greetFn(); // Hi, I'm undefined (lost `this` context!)
Method Invocation Patterns
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Method Invocation Patterns ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā 1. Object Method Invocation ā
ā obj.method() ā this = obj ā
ā ā
ā 2. Explicit Binding ā
ā method.call(obj) ā this = obj ā
ā method.apply(obj) ā this = obj ā
ā method.bind(obj)() ā this = obj ā
ā ā
ā 3. Implicit Binding (standalone call) ā
ā method() ā this = undefined (strict mode) ā
ā this = window (non-strict) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Example: Different Invocation Patterns
const obj = {
name: 'MyObject',
show() {
console.log(this?.name || 'No context');
},
};
// 1. Method invocation
obj.show(); // "MyObject"
// 2. Explicit binding
const standalone = obj.show;
standalone.call(obj); // "MyObject"
standalone.apply(obj); // "MyObject"
const bound = standalone.bind(obj);
bound(); // "MyObject"
// 3. Lost context
standalone(); // "No context" (undefined in strict mode)
Arrow Functions as Methods
ā ļø Warning: Arrow functions don't have their own this!
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Arrow Functions vs Regular Methods ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Regular Method: ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā const obj = { ā ā
ā ā name: "Object", ā ā
ā ā greet() { ā ā
ā ā console.log(this.name); ā ā ā
ā ā } ā ā
ā ā }; ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā Arrow Function (DON'T DO THIS): ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā const obj = { ā ā
ā ā name: "Object", ā ā
ā ā greet: () => { ā ā
ā ā console.log(this.name); ā ā ā
ā ā } // `this` is from outer scope! ā ā
ā ā }; ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
When Arrow Functions ARE Useful in Objects
const counter = {
count: 0,
// Regular method
start() {
// Arrow function INSIDE method preserves `this`
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
},
};
counter.start(); // Works correctly!
Chaining Methods
Method chaining allows multiple method calls in a single statement by returning this.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Method Chaining Pattern ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā builder.setName("Product") ā
ā .setPrice(99.99) ā Each method returns `this` ā
ā .setCategory("Tech") ā
ā .build(); ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā Flow Diagram ā ā
ā ā ā ā
ā ā builder ā setName() ā returns builder ā ā
ā ā ā setPrice() ā returns builder ā ā
ā ā ā setCategory() ā returns builder ā ā
ā ā ā build() ā returns final product ā ā
ā ā ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Implementing Method Chaining
const queryBuilder = {
query: '',
select(fields) {
this.query += `SELECT ${fields} `;
return this; // Return this for chaining
},
from(table) {
this.query += `FROM ${table} `;
return this;
},
where(condition) {
this.query += `WHERE ${condition} `;
return this;
},
build() {
return this.query.trim();
},
};
const sql = queryBuilder.select('*').from('users').where('age > 18').build();
console.log(sql); // "SELECT * FROM users WHERE age > 18"
Computed Method Names
ES6 allows using expressions as method names:
const prefix = 'get';
const property = 'Name';
const obj = {
name: 'Dynamic',
// Computed method name
[`${prefix}${property}`]() {
return this.name;
},
[`set${property}`](value) {
this.name = value;
},
};
console.log(obj.getName()); // "Dynamic"
obj.setName('Updated');
console.log(obj.getName()); // "Updated"
Use Cases for Computed Method Names
// Creating methods from an array
const operations = ['add', 'subtract', 'multiply', 'divide'];
const mathOps = {
value: 10,
...operations.reduce((acc, op) => {
acc[`${op}By`] = function (n) {
switch (op) {
case 'add':
this.value += n;
break;
case 'subtract':
this.value -= n;
break;
case 'multiply':
this.value *= n;
break;
case 'divide':
this.value /= n;
break;
}
return this;
};
return acc;
}, {}),
};
mathOps.addBy(5).multiplyBy(2).subtractBy(10);
console.log(mathOps.value); // 20
Best Practices
1. Use Method Shorthand
// ā Good
const obj = {
method() {},
};
// ā Avoid
const obj = {
method: function () {},
};
2. Keep Methods Focused
// ā Good - Single responsibility
const user = {
validate() {
/* validation only */
},
save() {
/* saving only */
},
notify() {
/* notification only */
},
};
// ā Avoid - Method doing too much
const user = {
validateAndSaveAndNotify() {
/* everything */
},
};
3. Return Meaningful Values
// ā Good - Return this for chaining, or useful value
const cart = {
items: [],
add(item) {
this.items.push(item);
return this; // Enable chaining
},
getTotal() {
return this.items.reduce((sum, item) => sum + item.price, 0);
},
};
4. Handle Missing Context
const obj = {
name: 'Object',
greet() {
// Defensive: handle potential undefined this
if (!this || !this.name) {
console.log('Hello, anonymous');
return;
}
console.log(`Hello, ${this.name}`);
},
};
Common Pitfalls
1. Losing this Context
const user = {
name: 'Alice',
greet() {
console.log(`Hello, ${this.name}`);
},
};
// ā Problem: Lost context
const greet = user.greet;
greet(); // undefined
// ā Solution 1: Use bind
const boundGreet = user.greet.bind(user);
boundGreet(); // "Hello, Alice"
// ā Solution 2: Arrow function wrapper
const arrowGreet = () => user.greet();
arrowGreet(); // "Hello, Alice"
2. Arrow Functions as Methods
// ā Don't use arrow functions for methods
const obj = {
value: 42,
getValue: () => this.value, // Wrong! `this` is not obj
};
// ā Use regular methods
const obj = {
value: 42,
getValue() {
return this.value;
},
};
3. Callback Context Loss
const counter = {
count: 0,
increment() {
this.count++;
},
// ā Problem
startBad() {
setTimeout(this.increment, 1000); // Loses context
},
// ā Solution 1: Bind
startWithBind() {
setTimeout(this.increment.bind(this), 1000);
},
// ā Solution 2: Arrow function
startWithArrow() {
setTimeout(() => this.increment(), 1000);
},
};
4. Nested Method Definitions
const obj = {
outer() {
// ā Problem: `this` changes in nested function
function inner() {
console.log(this); // undefined or window
}
inner();
// ā Solution: Arrow function or save reference
const that = this;
function innerFixed() {
console.log(that); // obj
}
const arrowInner = () => {
console.log(this); // obj
};
},
};
Method Types Summary
| Type | Syntax | this Binding | Use Case |
|---|---|---|---|
| Shorthand | method() {} | Dynamic | Default choice |
| Traditional | method: function() {} | Dynamic | Legacy code |
| Arrow | method: () => {} | Lexical | Never for methods |
| Getter | get prop() {} | Dynamic | Computed properties |
| Setter | set prop(v) {} | Dynamic | Property validation |
Files in This Section
- ā¢
README.md- This documentation - ā¢
examples.js- Runnable code examples - ā¢
exercises.js- Practice exercises with solutions
Navigation
- ā¢Previous: 7.1 Object Basics
- ā¢Next: 7.3 Property Descriptors
- ā¢Back to Module 7