javascript
examples
examples.js⚡javascript
/**
* =====================================================
* 7.2 OBJECT METHODS - EXAMPLES
* =====================================================
* Comprehensive examples demonstrating object methods in JavaScript
*/
// =====================================================
// EXAMPLE 1: Defining Methods - Traditional vs Shorthand
// =====================================================
console.log('=== Example 1: Defining Methods ===');
// Traditional function expression (ES5)
const calculatorTraditional = {
value: 0,
add: function (num) {
this.value += num;
return this.value;
},
subtract: function (num) {
this.value -= num;
return this.value;
},
};
// ES6 Method shorthand (preferred)
const calculatorModern = {
value: 0,
add(num) {
this.value += num;
return this.value;
},
subtract(num) {
this.value -= num;
return this.value;
},
};
console.log('Traditional:', calculatorTraditional.add(10)); // 10
console.log('Modern:', calculatorModern.add(10)); // 10
// =====================================================
// EXAMPLE 2: The `this` Keyword in Methods
// =====================================================
console.log('\n=== Example 2: The `this` Keyword ===');
const user = {
firstName: 'John',
lastName: 'Doe',
age: 30,
getFullName() {
return `${this.firstName} ${this.lastName}`;
},
greet() {
console.log(
`Hello, I'm ${this.getFullName()} and I'm ${this.age} years old.`
);
},
haveBirthday() {
this.age++;
console.log(`Happy birthday! Now ${this.age} years old.`);
},
};
console.log(user.getFullName()); // "John Doe"
user.greet(); // "Hello, I'm John Doe and I'm 30 years old."
user.haveBirthday(); // "Happy birthday! Now 31 years old."
// =====================================================
// EXAMPLE 3: `this` Context is Dynamic
// =====================================================
console.log('\n=== Example 3: Dynamic `this` Context ===');
const person1 = {
name: 'Alice',
introduce() {
console.log(`Hi, I'm ${this.name}`);
},
};
const person2 = {
name: 'Bob',
};
// Method borrowing - `this` depends on what's before the dot
person1.introduce(); // "Hi, I'm Alice"
// Assign the method to person2
person2.introduce = person1.introduce;
person2.introduce(); // "Hi, I'm Bob"
// Extract the method - loses context
const intro = person1.introduce;
// intro(); // Would be: "Hi, I'm undefined" (in strict mode)
// =====================================================
// EXAMPLE 4: Preserving `this` with bind()
// =====================================================
console.log('\n=== Example 4: Preserving `this` with bind() ===');
const counter = {
count: 0,
increment() {
this.count++;
console.log(`Count: ${this.count}`);
},
};
// Without bind - loses context
const incrementFn = counter.increment;
// incrementFn(); // Would fail or give wrong result
// With bind - preserves context
const boundIncrement = counter.increment.bind(counter);
boundIncrement(); // "Count: 1"
boundIncrement(); // "Count: 2"
// =====================================================
// EXAMPLE 5: call() and apply()
// =====================================================
console.log('\n=== Example 5: call() and apply() ===');
function introduce(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}
const person = { name: 'Charlie' };
// call() - pass arguments individually
introduce.call(person, 'Hello', '!'); // "Hello, I'm Charlie!"
// apply() - pass arguments as array
introduce.apply(person, ['Hi', '.']); // "Hi, I'm Charlie."
// Another example with object method
const mathHelper = {
multiply(a, b) {
return a * b * this.factor;
},
};
const context1 = { factor: 2 };
const context2 = { factor: 10 };
console.log(mathHelper.multiply.call(context1, 3, 4)); // 24 (3 * 4 * 2)
console.log(mathHelper.multiply.call(context2, 3, 4)); // 120 (3 * 4 * 10)
// =====================================================
// EXAMPLE 6: Arrow Functions vs Regular Methods
// =====================================================
console.log('\n=== Example 6: Arrow Functions vs Regular Methods ===');
// DON'T DO THIS - Arrow function as method
const badExample = {
name: 'Bad Object',
greet: () => {
// `this` is NOT badExample, it's the outer scope's this!
console.log(`Hello from ${this?.name || 'undefined'}`);
},
};
badExample.greet(); // "Hello from undefined"
// DO THIS - Regular method
const goodExample = {
name: 'Good Object',
greet() {
console.log(`Hello from ${this.name}`);
},
};
goodExample.greet(); // "Hello from Good Object"
// =====================================================
// EXAMPLE 7: Arrow Functions INSIDE Methods (Good!)
// =====================================================
console.log('\n=== Example 7: Arrow Functions Inside Methods ===');
const timer = {
seconds: 0,
start() {
console.log('Timer started');
// Arrow function preserves outer `this`
const interval = setInterval(() => {
this.seconds++;
console.log(`Seconds: ${this.seconds}`);
if (this.seconds >= 3) {
clearInterval(interval);
console.log('Timer stopped');
}
}, 100); // Fast for demo
},
};
// timer.start(); // Uncomment to run
// =====================================================
// EXAMPLE 8: Method Chaining
// =====================================================
console.log('\n=== Example 8: Method Chaining ===');
const stringBuilder = {
value: '',
append(str) {
this.value += str;
return this; // Return this for chaining
},
prepend(str) {
this.value = str + this.value;
return this;
},
uppercase() {
this.value = this.value.toUpperCase();
return this;
},
lowercase() {
this.value = this.value.toLowerCase();
return this;
},
trim() {
this.value = this.value.trim();
return this;
},
build() {
return this.value;
},
};
const result = stringBuilder
.append(' Hello ')
.append('World ')
.trim()
.uppercase()
.build();
console.log(result); // "HELLO WORLD"
// =====================================================
// EXAMPLE 9: Fluent API Pattern
// =====================================================
console.log('\n=== Example 9: Fluent API Pattern ===');
const queryBuilder = {
_table: '',
_fields: '*',
_conditions: [],
_orderBy: '',
_limit: null,
reset() {
this._table = '';
this._fields = '*';
this._conditions = [];
this._orderBy = '';
this._limit = null;
return this;
},
select(fields) {
this._fields = Array.isArray(fields) ? fields.join(', ') : fields;
return this;
},
from(table) {
this._table = table;
return this;
},
where(condition) {
this._conditions.push(condition);
return this;
},
orderBy(field, direction = 'ASC') {
this._orderBy = `${field} ${direction}`;
return this;
},
limit(n) {
this._limit = n;
return this;
},
build() {
let query = `SELECT ${this._fields} FROM ${this._table}`;
if (this._conditions.length > 0) {
query += ` WHERE ${this._conditions.join(' AND ')}`;
}
if (this._orderBy) {
query += ` ORDER BY ${this._orderBy}`;
}
if (this._limit !== null) {
query += ` LIMIT ${this._limit}`;
}
return query;
},
};
const sql = queryBuilder
.reset()
.select(['id', 'name', 'email'])
.from('users')
.where('age > 18')
.where("status = 'active'")
.orderBy('name', 'ASC')
.limit(10)
.build();
console.log(sql);
// SELECT id, name, email FROM users WHERE age > 18 AND status = 'active' ORDER BY name ASC LIMIT 10
// =====================================================
// EXAMPLE 10: Computed Method Names
// =====================================================
console.log('\n=== Example 10: Computed Method Names ===');
const prefix = 'get';
const suffix = 'Value';
const dynamicMethods = {
_name: 'Dynamic Object',
_value: 42,
// Computed method names using template literals
[`${prefix}Name`]() {
return this._name;
},
[`${prefix}${suffix}`]() {
return this._value;
},
[`set${suffix}`](val) {
this._value = val;
},
};
console.log(dynamicMethods.getName()); // "Dynamic Object"
console.log(dynamicMethods.getValue()); // 42
dynamicMethods.setValue(100);
console.log(dynamicMethods.getValue()); // 100
// =====================================================
// EXAMPLE 11: Methods from Symbol Keys
// =====================================================
console.log('\n=== Example 11: Symbol Method Keys ===');
const LOG = Symbol('log');
const VALIDATE = Symbol('validate');
const obj = {
data: 'Hello',
[LOG]() {
console.log(`[LOG] Data: ${this.data}`);
},
[VALIDATE]() {
return this.data.length > 0;
},
process() {
if (this[VALIDATE]()) {
this[LOG]();
return 'Processed successfully';
}
return 'Validation failed';
},
};
console.log(obj.process()); // [LOG] Data: Hello \n "Processed successfully"
// Symbol methods are not enumerable
console.log(Object.keys(obj)); // ["data", "process"]
// =====================================================
// EXAMPLE 12: Methods with Default Parameters
// =====================================================
console.log('\n=== Example 12: Methods with Default Parameters ===');
const formatter = {
format(value, options = {}) {
const {
prefix = '',
suffix = '',
uppercase = false,
truncate = null,
} = options;
let result = String(value);
if (truncate && result.length > truncate) {
result = result.substring(0, truncate) + '...';
}
if (uppercase) {
result = result.toUpperCase();
}
return `${prefix}${result}${suffix}`;
},
};
console.log(formatter.format('hello'));
// "hello"
console.log(formatter.format('hello', { uppercase: true }));
// "HELLO"
console.log(
formatter.format('hello world', { prefix: '>>> ', suffix: ' <<<' })
);
// ">>> hello world <<<"
console.log(formatter.format('This is a very long message', { truncate: 10 }));
// "This is a ..."
// =====================================================
// EXAMPLE 13: Rest Parameters in Methods
// =====================================================
console.log('\n=== Example 13: Rest Parameters in Methods ===');
const mathOperations = {
sum(...numbers) {
return numbers.reduce((total, n) => total + n, 0);
},
average(...numbers) {
if (numbers.length === 0) return 0;
return this.sum(...numbers) / numbers.length;
},
max(...numbers) {
return Math.max(...numbers);
},
min(...numbers) {
return Math.min(...numbers);
},
stats(...numbers) {
return {
sum: this.sum(...numbers),
average: this.average(...numbers),
max: this.max(...numbers),
min: this.min(...numbers),
count: numbers.length,
};
},
};
console.log(mathOperations.sum(1, 2, 3, 4, 5)); // 15
console.log(mathOperations.average(1, 2, 3, 4, 5)); // 3
console.log(mathOperations.stats(1, 2, 3, 4, 5));
// { sum: 15, average: 3, max: 5, min: 1, count: 5 }
// =====================================================
// EXAMPLE 14: Private Methods Pattern (using closures)
// =====================================================
console.log('\n=== Example 14: Private Methods Pattern ===');
function createBankAccount(initialBalance) {
// Private variable
let balance = initialBalance;
// Private function
function validateAmount(amount) {
return typeof amount === 'number' && amount > 0;
}
function logTransaction(type, amount) {
console.log(`[${type}] Amount: $${amount}, Balance: $${balance}`);
}
// Return object with public methods
return {
deposit(amount) {
if (!validateAmount(amount)) {
throw new Error('Invalid amount');
}
balance += amount;
logTransaction('DEPOSIT', amount);
return this;
},
withdraw(amount) {
if (!validateAmount(amount)) {
throw new Error('Invalid amount');
}
if (amount > balance) {
throw new Error('Insufficient funds');
}
balance -= amount;
logTransaction('WITHDRAW', amount);
return this;
},
getBalance() {
return balance;
},
};
}
const account = createBankAccount(100);
account
.deposit(50) // [DEPOSIT] Amount: $50, Balance: $150
.withdraw(30); // [WITHDRAW] Amount: $30, Balance: $120
console.log(`Final balance: $${account.getBalance()}`); // $120
// =====================================================
// EXAMPLE 15: Methods That Return New Objects
// =====================================================
console.log('\n=== Example 15: Methods Returning New Objects ===');
const Vector = {
create(x, y) {
return {
x,
y,
add(other) {
return Vector.create(this.x + other.x, this.y + other.y);
},
subtract(other) {
return Vector.create(this.x - other.x, this.y - other.y);
},
scale(factor) {
return Vector.create(this.x * factor, this.y * factor);
},
magnitude() {
return Math.sqrt(this.x ** 2 + this.y ** 2);
},
normalize() {
const mag = this.magnitude();
return mag === 0 ? Vector.create(0, 0) : this.scale(1 / mag);
},
toString() {
return `Vector(${this.x}, ${this.y})`;
},
};
},
};
const v1 = Vector.create(3, 4);
const v2 = Vector.create(1, 2);
console.log(v1.toString()); // Vector(3, 4)
console.log(v1.magnitude()); // 5
console.log(v1.add(v2).toString()); // Vector(4, 6)
console.log(v1.scale(2).toString()); // Vector(6, 8)
console.log(v1.normalize().toString()); // Vector(0.6, 0.8)
// =====================================================
// EXAMPLE 16: Async Methods
// =====================================================
console.log('\n=== Example 16: Async Methods ===');
const apiClient = {
baseUrl: 'https://api.example.com',
async get(endpoint) {
console.log(`Fetching ${this.baseUrl}${endpoint}`);
// Simulated API response
return { data: { id: 1, name: 'Example' } };
},
async post(endpoint, data) {
console.log(`Posting to ${this.baseUrl}${endpoint}`, data);
return { success: true, id: Date.now() };
},
async fetchUser(id) {
const response = await this.get(`/users/${id}`);
return response.data;
},
async createUser(userData) {
const response = await this.post('/users', userData);
return response;
},
};
// Using async methods
(async () => {
const user = await apiClient.fetchUser(1);
console.log('Fetched user:', user);
const created = await apiClient.createUser({ name: 'New User' });
console.log('Created:', created);
})();
// =====================================================
// EXAMPLE 17: Generator Methods
// =====================================================
console.log('\n=== Example 17: Generator Methods ===');
const range = {
start: 0,
end: 10,
step: 1,
*[Symbol.iterator]() {
for (let i = this.start; i <= this.end; i += this.step) {
yield i;
}
},
*filter(predicate) {
for (const value of this) {
if (predicate(value)) {
yield value;
}
}
},
*map(transform) {
for (const value of this) {
yield transform(value);
}
},
toArray() {
return [...this];
},
};
console.log(range.toArray()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// Using generator methods
const evenNumbers = [...range.filter((n) => n % 2 === 0)];
console.log('Even:', evenNumbers); // [0, 2, 4, 6, 8, 10]
const squares = [...range.map((n) => n * n)];
console.log('Squares:', squares); // [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
// =====================================================
// EXAMPLE 18: Method Decorators Pattern
// =====================================================
console.log('\n=== Example 18: Method Decorators Pattern ===');
// Simple logging decorator
function withLogging(obj, methodName) {
const original = obj[methodName];
obj[methodName] = function (...args) {
console.log(`[CALL] ${methodName}(${args.join(', ')})`);
const result = original.apply(this, args);
console.log(`[RETURN] ${methodName} => ${JSON.stringify(result)}`);
return result;
};
return obj;
}
// Memoization decorator
function withMemoization(obj, methodName) {
const original = obj[methodName];
const cache = new Map();
obj[methodName] = function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log(`[CACHE HIT] ${methodName}(${args.join(', ')})`);
return cache.get(key);
}
const result = original.apply(this, args);
cache.set(key, result);
return result;
};
return obj;
}
const mathService = {
factorial(n) {
if (n <= 1) return 1;
return n * this.factorial(n - 1);
},
};
// Apply decorators
withLogging(mathService, 'factorial');
withMemoization(mathService, 'factorial');
console.log(mathService.factorial(5)); // 120 (with logging)
console.log(mathService.factorial(5)); // 120 (from cache)
// =====================================================
// EXAMPLE 19: State Machine with Methods
// =====================================================
console.log('\n=== Example 19: State Machine with Methods ===');
const trafficLight = {
states: ['red', 'yellow', 'green'],
currentIndex: 0,
get currentState() {
return this.states[this.currentIndex];
},
next() {
this.currentIndex = (this.currentIndex + 1) % this.states.length;
console.log(`Light changed to: ${this.currentState}`);
return this;
},
reset() {
this.currentIndex = 0;
console.log(`Light reset to: ${this.currentState}`);
return this;
},
canGo() {
return this.currentState === 'green';
},
shouldStop() {
return this.currentState === 'red';
},
};
console.log(`Current: ${trafficLight.currentState}`); // red
trafficLight.next(); // yellow
trafficLight.next(); // green
console.log(`Can go: ${trafficLight.canGo()}`); // true
trafficLight.next(); // red
console.log(`Should stop: ${trafficLight.shouldStop()}`); // true
// =====================================================
// EXAMPLE 20: Object with Validation Methods
// =====================================================
console.log('\n=== Example 20: Validation Methods ===');
const userValidator = {
errors: [],
reset() {
this.errors = [];
return this;
},
validateName(name) {
if (!name || name.trim().length < 2) {
this.errors.push('Name must be at least 2 characters');
}
return this;
},
validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
this.errors.push('Invalid email format');
}
return this;
},
validateAge(age) {
if (typeof age !== 'number' || age < 0 || age > 150) {
this.errors.push('Age must be a number between 0 and 150');
}
return this;
},
validatePassword(password) {
if (!password || password.length < 8) {
this.errors.push('Password must be at least 8 characters');
}
if (!/[A-Z]/.test(password)) {
this.errors.push('Password must contain uppercase letter');
}
if (!/[0-9]/.test(password)) {
this.errors.push('Password must contain a number');
}
return this;
},
isValid() {
return this.errors.length === 0;
},
getErrors() {
return [...this.errors];
},
validate(user) {
return this.reset()
.validateName(user.name)
.validateEmail(user.email)
.validateAge(user.age)
.validatePassword(user.password);
},
};
// Test validation
const validUser = {
name: 'John Doe',
email: 'john@example.com',
age: 30,
password: 'SecurePass123',
};
userValidator.validate(validUser);
console.log('Valid user valid?', userValidator.isValid()); // true
const invalidUser = {
name: 'J',
email: 'invalid',
age: 200,
password: 'weak',
};
userValidator.validate(invalidUser);
console.log('Invalid user valid?', userValidator.isValid()); // false
console.log('Errors:', userValidator.getErrors());
console.log('\n=== All Examples Complete ===');