javascript
examples
examples.js⚡javascript
/**
* =====================================================
* 5.5 CALL, APPLY, AND BIND - EXAMPLES
* =====================================================
* Controlling function context
*/
// =====================================================
// 1. UNDERSTANDING 'THIS' CONTEXT
// =====================================================
console.log("--- Understanding 'this' Context ---");
const user = {
name: 'Alice',
greet() {
console.log('Hello, ' + this.name);
},
};
// Method call - 'this' is the object
user.greet(); // "Hello, Alice"
// Extracted function - 'this' is lost
const greet = user.greet;
// greet(); // "Hello, undefined" (strict mode: error)
console.log('This is why we need call/apply/bind!');
// =====================================================
// 2. THE CALL() METHOD
// =====================================================
console.log('\n--- The call() Method ---');
function introduce(greeting, punctuation) {
console.log(`${greeting}, I'm ${this.name}${punctuation}`);
}
const person1 = { name: 'Alice' };
const person2 = { name: 'Bob' };
// Using call to specify 'this'
introduce.call(person1, 'Hello', '!'); // "Hello, I'm Alice!"
introduce.call(person2, 'Hi', '.'); // "Hi, I'm Bob."
// Simple example
function sayName() {
console.log('My name is ' + this.name);
}
sayName.call({ name: 'Charlie' }); // "My name is Charlie"
// =====================================================
// 3. THE APPLY() METHOD
// =====================================================
console.log('\n--- The apply() Method ---');
// Same function, using apply
introduce.apply(person1, ['Hey', '??']); // "Hey, I'm Alice??"
// apply is useful with arrays
const numbers = [5, 6, 2, 3, 7];
// Using apply with Math.max
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
console.log('Max:', max); // 7
console.log('Min:', min); // 2
// Modern alternative with spread
console.log('Max (spread):', Math.max(...numbers));
// =====================================================
// 4. CALL VS APPLY COMPARISON
// =====================================================
console.log('\n--- call() vs apply() ---');
function logInfo(role, department) {
console.log(`${this.name}: ${role} in ${department}`);
}
const employee = { name: 'Alice' };
// call: individual arguments (C = Comma)
logInfo.call(employee, 'Developer', 'Engineering');
// apply: array of arguments (A = Array)
logInfo.apply(employee, ['Developer', 'Engineering']);
// Using spread with call (modern preference)
const args = ['Manager', 'Sales'];
logInfo.call(employee, ...args);
// =====================================================
// 5. THE BIND() METHOD
// =====================================================
console.log('\n--- The bind() Method ---');
const user2 = {
name: 'Bob',
greet() {
console.log('Hello, ' + this.name);
},
};
// Without bind - context lost
const lostGreet = user2.greet;
// lostGreet(); // undefined
// With bind - context preserved
const boundGreet = user2.greet.bind(user2);
boundGreet(); // "Hello, Bob"
// bind vs call: bind returns function, call invokes immediately
console.log('\nbind returns a function:');
const fn = introduce.bind(person1);
console.log(typeof fn); // "function"
fn('Howdy', '!'); // "Howdy, I'm Alice!"
// =====================================================
// 6. BIND WITH PRESET ARGUMENTS
// =====================================================
console.log('\n--- bind() with Preset Arguments ---');
function multiply(a, b) {
return a * b;
}
// Create specialized functions
const double = multiply.bind(null, 2);
const triple = multiply.bind(null, 3);
const quadruple = multiply.bind(null, 4);
console.log('double(5):', double(5)); // 10
console.log('triple(5):', triple(5)); // 15
console.log('quadruple(5):', quadruple(5)); // 20
// With this context
function greetFormal(greeting, title) {
return `${greeting}, ${title} ${this.name}`;
}
const person = { name: 'Smith' };
const greetMr = greetFormal.bind(person, 'Good morning', 'Mr.');
const greetMs = greetFormal.bind(person, 'Good evening', 'Ms.');
console.log(greetMr()); // "Good morning, Mr. Smith"
console.log(greetMs()); // "Good evening, Ms. Smith"
// =====================================================
// 7. EVENT HANDLERS WITH BIND
// =====================================================
console.log('\n--- Event Handlers Pattern ---');
class Counter {
constructor(name) {
this.name = name;
this.count = 0;
// Bind method in constructor
this.increment = this.increment.bind(this);
}
increment() {
this.count++;
console.log(`${this.name}: ${this.count}`);
}
}
const counter = new Counter('MyCounter');
// Can pass as callback without losing context
counter.increment(); // "MyCounter: 1"
// Simulating event callback
const callback = counter.increment;
callback(); // "MyCounter: 2" (works because we bound in constructor)
// =====================================================
// 8. METHOD BORROWING
// =====================================================
console.log('\n--- Method Borrowing ---');
// Array-like object (not a real array)
const arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
};
// Borrow Array methods
const realArray = Array.prototype.slice.call(arrayLike);
console.log('Converted to array:', realArray);
const joined = Array.prototype.join.call(arrayLike, '-');
console.log('Joined:', joined); // "a-b-c"
// Using map on array-like
const mapped = Array.prototype.map.call(arrayLike, (x) => x.toUpperCase());
console.log('Mapped:', mapped); // ["A", "B", "C"]
// Modern alternative
const modern = Array.from(arrayLike);
console.log('Array.from:', modern);
// =====================================================
// 9. BORROWING OBJECT METHODS
// =====================================================
console.log('\n--- Borrowing Object Methods ---');
const obj = { a: 1, b: 2 };
// Safe hasOwnProperty
const hasOwn = Object.prototype.hasOwnProperty;
console.log("Has 'a':", hasOwn.call(obj, 'a'));
console.log("Has 'c':", hasOwn.call(obj, 'c'));
console.log("Has 'toString':", hasOwn.call(obj, 'toString'));
// toString borrowing
const num = 255;
const hexString = Number.prototype.toString.call(num, 16);
console.log('255 in hex:', hexString); // "ff"
// =====================================================
// 10. CONSTRUCTOR BORROWING
// =====================================================
console.log('\n--- Constructor Borrowing ---');
function Animal(name) {
this.name = name;
this.isAlive = true;
}
function Dog(name, breed) {
// Borrow Animal constructor
Animal.call(this, name);
this.breed = breed;
}
const myDog = new Dog('Rex', 'German Shepherd');
console.log('Dog:', myDog);
// { name: "Rex", isAlive: true, breed: "German Shepherd" }
// =====================================================
// 11. PARTIAL APPLICATION PATTERN
// =====================================================
console.log('\n--- Partial Application ---');
function log(level, timestamp, message) {
console.log(`[${level}] ${timestamp}: ${message}`);
}
// Create specialized loggers
const info = log.bind(null, 'INFO');
const error = log.bind(null, 'ERROR');
const debug = log.bind(null, 'DEBUG');
const now = new Date().toISOString();
info(now, 'Application started');
error(now, 'Connection failed');
debug(now, 'Debug info here');
// Pre-fill more arguments
const currentLog = log.bind(null, 'INFO', new Date().toISOString());
currentLog('This is pre-timestamped');
// =====================================================
// 12. SETTIMEOUT/SETINTERVAL WITH BIND
// =====================================================
console.log('\n--- setTimeout with bind ---');
const timer = {
name: 'MyTimer',
count: 0,
start() {
console.log(`${this.name} starting...`);
// Using bind
setTimeout(
function () {
this.count++;
console.log(`${this.name}: ${this.count}`);
}.bind(this),
100
);
},
startArrow() {
// Arrow function alternative
setTimeout(() => {
this.count++;
console.log(`${this.name} (arrow): ${this.count}`);
}, 200);
},
};
timer.start();
timer.startArrow();
// =====================================================
// 13. BIND ONLY WORKS ONCE
// =====================================================
console.log('\n--- Bind Only Works Once ---');
function showThis() {
console.log('this.name:', this?.name || 'undefined');
}
const bound1 = showThis.bind({ name: 'First' });
const bound2 = bound1.bind({ name: 'Second' }); // Ignored!
bound1(); // "First"
bound2(); // Still "First" - second bind is ignored
// =====================================================
// 14. USING CALL/APPLY WITH BUILT-INS
// =====================================================
console.log('\n--- Built-in Method Borrowing ---');
// Get type of value
function getType(value) {
return Object.prototype.toString.call(value);
}
console.log('null:', getType(null)); // [object Null]
console.log('array:', getType([])); // [object Array]
console.log('object:', getType({})); // [object Object]
console.log(
'function:',
getType(() => {})
); // [object Function]
console.log('date:', getType(new Date())); // [object Date]
// =====================================================
// 15. PRACTICAL EXAMPLE: MIXIN
// =====================================================
console.log('\n--- Mixin Pattern ---');
const eventMixin = {
on(event, handler) {
this._events = this._events || {};
(this._events[event] = this._events[event] || []).push(handler);
},
emit(event, ...args) {
(this._events?.[event] || []).forEach((handler) => {
handler.apply(this, args);
});
},
};
// Create an object and add event capabilities
const player = {
name: 'Player1',
score: 0,
};
Object.assign(player, eventMixin);
player.on('score', function (points) {
this.score += points;
console.log(`${this.name} scored! Total: ${this.score}`);
});
player.emit('score', 10);
player.emit('score', 25);
// =====================================================
// SUMMARY
// =====================================================
console.log('\n--- Summary ---');
console.log(`
call(context, arg1, arg2, ...)
• Invoke immediately
• Pass arguments individually
• fn.call(obj, 1, 2, 3)
apply(context, [args])
• Invoke immediately
• Pass arguments as array
• fn.apply(obj, [1, 2, 3])
bind(context, arg1, arg2, ...)
• Returns new function
• Context permanently bound
• Can preset arguments
• const bound = fn.bind(obj, 1)
Use Cases:
• Event handler binding
• Method borrowing
• Constructor borrowing
• Partial application
• setTimeout/setInterval
• Callback preservation
`);